洛谷 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; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮