这个题是老师上课讲的题,对我来说还能勉强接受,所以就写了个解题报告。
好了,先看题:
INPUT
四个正整数,n,m,p,q。
OUTPUT
n行,第i行表示第i个馒头的最终颜色
样例输入
4 3 2 4
样例输出
2
2
3
0
额,这个呢,就是用并查集倒过来做,先读入,然后从m到1来染色,一开始我也没有想到,因为倒过来做的话就可以肯定是最终颜色了,之后,我们再算区间(ip+q)%n+1及(iq+p)%n+!也就是L和R区间中,给没有染色的馒头染上色,然后将这个区间的被染色的馒头的父亲都指向下一个馒头,这样下一次染色假如又染到被染过的馒头(也就是下一个区间里又包含被用过的点的话)的话就可以直接跳过去,相信很多像我一样的蒟蒻看到这里都听懵了,没关系,好好想想其实可以勉强接受的,并竟我也是一个并查集刚入门的蒟蒻,其实你只要不要想什么(i*p+q)%n+1什么的,只要随便举出几个区间,画一画就能明白。
当然,如果不知道具体写法的话,我可以在标程前给你们一个具体模版:
for i=m;i>=1;i–
for(int ii=getfather(l);ii<=r;ii=getfather(i))
[
ans[ii]=i;
fa[ii]=ii+1;
}
以下是标程:
#include<cstdio>
#include<iostream>
using namespace std;
int n,m,p,q,fa[1000111],ans[1000111];
int get(int x)//并查集
{
if(fa[x]!=x)
return fa[x]=get(fa[x]);
else
return x;
}
void ready()//初始化。
{
for(int i=1;i<=n+1;i++)
{
fa[i]=i;
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&p,&q);
ready();
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 ii=get(l);ii<=r;ii=get(ii))//至关重要的染色操作
{
ans[ii]=i;//保存最后一次染色
fa[ii]=ii+1;//将ii点指向后一个点,向后染色,可以避免染到染过的(另一个区间的)馒头
}
}
for(int i=1;i<=n;i++)
{
//printf("%d\n",fa[i]);
printf("%d\n",ans[i]);
}
return 0;
}
好了,如果不嫌弃的话,可以看看我准备写的关于并查集虚点的博客,当然因为我只是个入门,所以肯定也是十分经典而又蒟蒻(对你们这些神犇来说)的题。