Luogu P2391 白雪皑皑 题解 [ 蓝 ] [ 并查集 ] [ 区间覆盖 ] [ 倒序操作 ]
白雪皑皑:小清新并查集维护连通性的题。
注意到本题是区间覆盖操作,因此每个位置的颜色只会被最后一次区间覆盖决定,于是倒序枚举区间覆盖操作,将没有颜色的位置覆盖。
但是这样从左到右一个一个枚举染色显然会超时。考虑如何快速覆盖没有颜色的位置,因为每个数只会被最后那一次染色,所以已经覆盖了的位置对我们而言没有任何用处,可以直接跳过。于是我们需要保证每次染色都只会跳到未被染色的点,就能让均摊复杂度正确。
为了从一个点开始跳到下一个未被染色的点,可以利用并查集维护序列中每个点下一个未被染色的点,每次进行点覆盖之后合并这个点和下一个点即可。
时间复杂度 \(O(n)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
const int N=1000005;
int fa[N],n,m,p,q,c[N];
int findf(int x)
{
if(fa[x]!=x)fa[x]=findf(fa[x]);
return fa[x];
}
void combine(int x,int y)
{
int fx=findf(x),fy=findf(y);
fa[fx]=fy;
}
void init()
{
for(int i=1;i<=n+1;i++)fa[i]=i;
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>p>>q;
init();
for(int i=m;i>=1;i--)
{
int l=((i*p+q)%n)+1,r=((i*q+p)%n)+1;
if(l>r)swap(l,r);
int p=findf(l);
while(p<=r)
{
c[p]=i;
combine(p,p+1);
p=findf(p);
}
}
for(int i=1;i<=n;i++)cout<<c[i]<<'\n';
return 0;
}

浙公网安备 33010602011771号