并查集经典应用之染色模型

3115. 疯狂的馒头 - AcWing题库

思路:

由于一个点最后的颜色取决于最后一次染色,那么我们可以倒着染色, 每染色一个点就将这个点删掉并记录颜色。

ptr[i] 表示第i 个元素后面第一个未被覆盖的区间

当我们覆盖一个点i 的时候,就把i的指针ptr 向后移动到i+1 的指针指向的位置(指针跳跃)

这样就不会重复覆盖一个点

另外需要注意的是,对于点n+1,我们需要初始化它的指针,否则会无限调用ptr:因为ptr[n+1]=0,而p[0]=0。不过我们也可以通过初始化所有ptr避免这个问题

参考:AcWing 3115. 疯狂的馒头【并查集区间染色模型 / 线段树剪枝】 - AcWing

#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;
}

 

posted @ 2022-05-05 08:41  光風霽月  阅读(28)  评论(0编辑  收藏  举报