2019.9.5晚刷题记录
\(1.bzoj\) \(1202\) 狡猾的商人
(类似3714 Kuglarz)
做法:带权并查集
错误原因:快读写错了
用时:40min
分析:
还是利用前缀和的思想,\(num[i,j]=sum [j]-sum [i-1]\).
如果暴力维护,不能维护到所有的信息,例:
\(num[1,3]=4 ,num[1,2]=2 ,num[2,3]=5\)是错误的,但无法判断出来,但是从这里我们发现了它们之间的关系是具有传递性的,所以我们可以考虑使用并查集。
我们记\(d[x]\)表示\(sum[x]-sum[ fa [x]]\).
首先要注意的是输入\(s,t\)时,\(--s\)。
其次是需要在每次\(find\)的时候增加一些新的操作
int find(int x) { if(x==fa[x]) return fa[x]; int t=find(fa[x]); d[x]+=d[fa[x]];//不能写为d[x]+=d[t],因为这样中间跳过了许多步骤,我们的到新的d[x]的正确过程 fa[x]=t; //应该是sum[x]-sum[fa[x]]+ return fa[x]; //sum[fa[x]]-sum[fa[fa[x]]]+...+sum[son[t]]-sum[t] }
另外要注意的就是在每次输入关系时,若s-1与t在同一个集合中,则直接查询\(num[s,t]=sum[t]-sum[s-1]\) (我们在输入时已经\(--s\)了,所以这里不用再\(-1\))\(=d[t]-d[s-1]\).
若两者在不同的集合中,就将它们进行合并
int fu=find(u),fv=find(v); if(fu!=fv) { fa[fv]=fu; d[fv]=d[u]+w-d[v];//d[fv]=sum[fv]-sum[fa[fv]]=sum[fv]-sum[fu]=sum[u]-sum[fu]- //sum[v]+sum[fv]+w,又因为sum[v]-sum[u]=w //其实并不需要关心是fu大还是fv大,因为这样的出来的结果依然是正确的,相当于是sum[1]-sum[3]=- //(sum[3]-sum[1]) }
\(2.bzoj\) \(1004\) \(Cards\)
做法:burnside引理,polya定理,置换
错误:
因为每个排列都要重新计算不动点的个数,所以那些dp里要用到的东西基本上都要每次清零(然而我并没有)
用时:60min
坑点一:算循环节的个数,我是直接用\(tarjan\)算强联通分量的个数,然而事实是一个很简单的dfs就可以了(其实这个说不上是坑点)
int cnt=0; for(int i=1;i<=n;++i) { if(!vis[i]) { int sz=0; while(!vis[i]) vis[i]=1,sz++,i=a[i]; sum[++cnt]=sz; } }
坑点二:实际上它不能直接上不动点的个数方程\(C(f)=kxor(m(f))\),因为这里的颜色个数是有限制的,不过解决方法也很简单,用一个三维的\(dp\)方程就可以了。
我们设\(f [i] [j] [k]\)表示红色选了\(i\)个,蓝色选了\(j\)个,绿色选了\(k\)个的方案总数,最后\(C(f)=f [sr] [sb] [sg]\)。
for(int i=1;i<=cnt;++i) { for(int j=sr;j>=0;--j) { for(int k=sb;k>=0;--k) { for(int h=sg;h>=0;--h) { if(j>=sum[i]) f[j][k][h]=(f[j][k][h]+f[j-sum[i]][k][h])%p; if(k>=sum[i]) f[j][k][h]=(f[j][k][h]+f[j][k-sum[i]][h])%p; if(h>=sum[i]) f[j][k][h]=(f[j][k][h]+f[j][k][h-sum[i]])%p; } } } } return f[sr][sb][sg];
坑点三:不动也是一个置换,还要算上它的不动点个数,最后是乘上\(m+1\)的逆元(其实这不算是一个坑点:所有这类的题都是这样的,会除上\((m+1)\))。