BZOJ 2054 疯狂的馒头
【Description】
n个点,m次操作,每次把(i*p+q)%n+1与(i*q+p)%n+1之间的点染上颜色i,被染过色的会被新颜色覆盖,求最后每个点的颜色。
【题解】
因为被染过的颜色会被新的颜色覆盖,所以对于一个点,有效的染色只是最后一次。
那么我们可以倒着染色,已经染色的不染
求每个点被哪种区间覆盖或者某个区间是否已经被覆盖过都可以用并查集做。
我们把每个染过色的点都指向染色区间的右端点+1的位置,那么下一个没被染色的点就是find(x)
如果一个区间[l,r]满足find(l)>=r+1,那么这个区间已经被染色
#include<cstdio> #include<algorithm> using namespace std; const int maxn=1000010; int n,m,p,q,col[maxn],fa[maxn]; void read(int &k){ k=0; int f=1; char c=getchar(); while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar(); k*=f; } int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} int main(){ read(n); read(m); read(p); read(q); for (int i=1;i<=n+1;i++) fa[i]=i; for (int i=m;i>=1;i--){ int l=(1LL*i*p+q)%n+1,r=(1LL*i*q+p)%n+1; if (l>r) swap(l,r); for (int j=find(l);j<=r;j=find(j)){ col[j]=i; fa[j]=find(j+1); } } for (int i=1;i<=n;i++) printf("%d\n",col[i]); return 0; }