Bzoj P2054 疯狂的馒头 | 并查集
思路:因为每次染色都会将某些馒头的颜色彻底更改,所以每个馒头的最终的颜色其实是由最后一次染色决定的,那么我们只考虑最后一次染色即可。对此,我们可以从后往前倒着染色,当目前的染色区间中存在白色馒头时,就将其染成当前的颜色,对于已经染过色的馒头则不处理,因为当前这一次染色已经不是其最后一次染色了,其最终颜色已经确定了。
对以上思路,考虑用经过路径压缩优化的并查集来维护在每个馒头右侧的、距离最近的、未染色的馒头的位置,以减小时间复杂度。
总时间复杂度O(m+n)。
代码:
#include<iostream> #include<cstdio> using namespace std; int c[1000005],f[1000005]; int find(int x) { if(f[x]==x) return x; return f[x]=find(f[x]);//路径压缩 } int main() { int n=0,m=0,p=0,q=0; scanf("%d%d%d%d",&n,&m,&p,&q); for(int i=1;i<=n+1;i++) f[i]=i;//初始化 for(int i=m;i>=1;i--) { int l=(i*p+q)%n+1,r=(i*q+p)%n+1; if(l>r) swap(l,r); for(int k=l;;) { int fx=find(k); k=f[fx];//向右找最近的未染色的馒头进行染色 if(k>r) break;//如果超过了染色区间范围则退出 c[k]=i;//染色 f[fx]=find(k+1);//向右合并,得出下一个最近的、在右边的未染色馒头的位置(画画图就明白了) } } for(int i=1;i<=n;i++) printf("%d\n",c[i]); return 0; }