洛谷 P2391.白雪皑皑 (并查集,思维)

时间:2022-03-31 09:12:30

洛谷 P2391.白雪皑皑  (并查集,思维)

  • 题意:有\(n\)个点,对这些点进行\(m\)次染色,第\(i\)次染色会把区间\((i*p+q)\ mod\ N+1\)和\((i*q+p)\ mod\ N+1\)之间的点染成颜色\(i\),问最后这\(n\)个点的颜色.

  • 题解:我们可以反着从第\(m\)次开始染,因为后面的会把前面点的颜色覆盖,所以倒着来的话,下一次染的时候就可以不用考虑已经染过的区间了,那么我们怎么维护染过的区间呢?我们可以把这些区间看成是一些连通块,所以可以用并查集来对区间进行维护,让区间内的所有点均指向它的右端点,具体操作看代码吧.

  • 代码:

    #include <iostream>
    #include <iomanip>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <vector>
    #include <map>
    #include <set>
    #include <unordered_set>
    #include <unordered_map>
    #define ll long long
    #define db double
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e7 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    int gcd(int a,int b){return b?gcd(b,a%b):a;}
    int lcm(int a,int b){return a/gcd(a,b)*b;} inline int read()
    {
    int X=0; bool flag=1; char ch=getchar();
    while(ch<'0'|ch>'9') {if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
    if(flag) return X;
    return ~(X-1);
    } int n,m,p,q;
    int P[N];
    int color[N]; int find(int x){
    if(P[x]!=x) P[x]=find(P[x]);
    return P[x];
    } int main() {
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>n>>m>>p>>q; rep(i,1,n) P[i]=i; P[n+1]=n+1; per(i,m,1){
    int l=(i*p+q)%n+1;
    int r=(i*q+p)%n+1;
    if(l>r) swap(l,r); l=find(l); //找左端点所在区间集合的右端点 while(l<=r){
    color[l]=i;
    P[l]=l+1; //每次让当前点的祖宗是它下一个点
    l=find(P[l+1]); //找下一个点的祖宗
    }
    } rep(i,1,n) cout<<color[i]<<'\n'; return 0;
    }