【并查集】疯狂的涂色

来源:互联网 发布:农村淘宝现状分析 编辑:程序博客网 时间:2024/05/16 10:16

疯狂的涂色


t非常喜爱画画,但是他还是一个初学者。他最近费尽千辛万苦才拜到小Q为师。小Q是画鸡蛋长大的,让小t一入门就拿着一张白纸条疯狂地涂色。假设纸条被划分成了n个区域,用1~n的整数从左到右顺序编号,小Q总共下达了m条指令。第i条指令是让小t把编号为(i*p+q)modn+1(i*q+p)mod n+1(p,q为常整数)之间的区域(连续的一段区域)涂成第i种颜色。

现在由于小Q下达的指令过多,小t一时应付不过来。小Q只让他回答每一个区域最后的颜色。趁小Q还在“五谷轮回之所”忙碌时,小t偷偷的请让你这个计算机高手帮他算出最后的颜色状态,并告诉他。


输入格式

文件仅一行,为四个整数n,m,p,q


输出格式

文件共n行,第i行代表最后第i个格子的颜色。白色编号为0


输入样例

4 32 4


输出样例

2

2

3

0


数据范围

20%数据满足:1n10001m10000

40%数据满足:1n100001m100000

100%数据满足:1n10000001m10000000;1m*p+qm*q+p231-1


这道题中,左右界周期性,所以需要找循环节,可以降低时间复杂度。。。这个最恼人了,到现在还没有掌握,虽然打出来了。

这道题真的有点难。除了考虑循环节之外,还要考虑一个尽量不重复刷。

因此本题正解是,从最后一刷往前枚举。因为只需要知道最后颜色状态,因此只需要刷最后一段,上一个周期重复的就不再刷。

倒着刷,就保证了先刷的是要保留的,因此后面遇到就不能再刷了,这个用一个并查集来维护,跟前段时间覃禹舜讲的一道题很相似,

也是用并查集维护下一个需要处理的点,(这种特殊的并查集必须要让旧点的父亲指向新点)。


另外这道题有点变态的地方,如果写普通的并查集要栈溢出,深度过大。因此要写一个手工的栈,模拟递归。。。

long t;
while (fa[a]!=a)
{
stack[++top] = a;
a=fa[a];
}
t = fa[a];

这部分递归求根,

while (top>0)
{
a = stack[top];
top--;
fa[a] = t;
}
return t;

这部分回溯压缩树的深度。



//#include <iostream>//using std::cout;//using std::cin;#include <cstdio>const long oo = 0x7fff0000;long c[1000002];long fa[1000002];long stack[1000002];long top = 0;long n;long m;long p;long q;long getroot(long a){long t;while (fa[a]!=a){stack[++top] = a;a=fa[a];}t = fa[a];while (top>0){a = stack[top];top--;fa[a] = t;}return t;}int main(){freopen("paint.in","r",stdin);freopen("paint.out","w",stdout);scanf("%ld%ld%ld%ld",&n,&m,&p,&q);long lm;long rm;for (long i=1;i<n+1;i++){fa[i] = i;}for (long i=m;i>0&&m-i+1<=n;i--){long l = (i*p+q)%n +1;long r = (i*q+p)%n +1;if (l>r){long tmp=l;l=r;r=tmp;}if(i==m){lm=(m*p+q)%n+1;rm=(m*q+p)%n+1;}else if(l==lm&&r==rm)break;for (long j=getroot(r);j>l-1;j=getroot(j)){if (c[j]==0)c[j] = i;fa[j] = j-1;}}for (long i=1;i<n+1;i++){printf("%ld\n",c[i]);}return 0;}


原创粉丝点击