洛谷 P2391.白雪皚皚 (並查集,思維)
阿新 • • 發佈:2020-11-13
-
題意:有\(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; }