并查集经典应用之染色模型
思路:
由于一个点最后的颜色取决于最后一次染色,那么我们可以倒着染色, 每染色一个点就将这个点删掉并记录颜色。
用ptr[i] 表示第i 个元素后面第一个未被覆盖的区间
当我们覆盖一个点i 的时候,就把i的指针ptr 向后移动到i+1 的指针指向的位置(指针跳跃)
这样就不会重复覆盖一个点
另外需要注意的是,对于点n+1,我们需要初始化它的指针,否则会无限调用ptr:因为ptr[n+1]=0,而p[0]=0。不过我们也可以通过初始化所有ptr避免这个问题
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000010;
int n, m, p, q;
int color[N], ptr[N];
void init()
{
for(int i = 0; i < N; i ++ ) ptr[i] = i;
}
int find(int x)
{
if(ptr[x] == x) return x;
return ptr[x] = find(ptr[x]);
}
int main()
{
init();
cin >> n >> m >> p >> q;
for(int i = m; i >= 1; i -- )//倒着染色
{
int a = (i * p + q) % n + 1, b = (i * q + p) % n + 1;
int r = max(a, b), l = min(a, b);//处理一下顺序
int pa = find(l);//找到指针指向的点
while(pa <= r)
{
color[pa] = i;//染色
ptr[pa] = find(pa + 1);//指针跳跃
pa = ptr[pa];//下一个点
}
}
//卡常数,输出必须用scanf
for(int i = 1; i <= n; i ++ ) printf("%d\n", color[i]);
return 0;
}