【题解】白雪皑皑
并查集这个东西的结构还是很简单的。
P2391 白雪皑皑
对于题目,大多数人肯定会想到线段树,但是这道题的 \(m\) 太大了,以 \(Seg\) \(tree\) 的 \(O(mlog_n^2)\) 的复杂度绝对会 T 的。
所以这道题可谓是一道并查集绝世好题啊。
并查集是用来干啥的呢,你先别急,后头再说。
我们首先将每个点的 \(fa\) 节点的祖先连向他自己。

然后从后向前遍历这个序列。
特注:为什莫要从后向前遍历这个序列呢?因为如果从前向后遍历的话后面一定有操作将前面操作的数覆盖,这样前面操作的就没用了。但如果从后向前遍历这个序列的话,那么只要在确定这个节点已经被覆盖后,就不用再次覆盖这个节点了,可见这是一个在覆盖问题中常见的优化技巧。
现在可以说为什莫要用并查集和并查集是干啥的了。并查集里存的祖先是啥呢,是在这个序列前面第一个是 \(0\) 的节点。
当有操作第一次覆盖这个节点时,我们将这个节点覆盖,然后将这个节点的 \(fa\) 指向 \(l+1\),或许你会觉得 \(l+1\) 也有可能不是 \(0\),但这没有丝毫的关系,因为在下一步,我们会路径压缩啊!之后便历下去就好了。

详见 \(Code\)。
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
const ll N=1e6+5;
ll fa[N],a[N];
ll n,m,p,q;
ll fd(ll x){
if(fa[x]==x)return x;
return fa[x]=fd(fa[x]);
}
int main(){
cin>>n>>m>>p>>q;
for(ll i=1;i<=n+1;i++)fa[i]=i;
for(ll i=m;i>=1;i--){
ll l=(i*p+q)%n+1;
ll r=(i*q+p)%n+1;
if(l>r)swap(l,r);
l=fd(l);
while(l<=r){
a[l]=i;
fa[l]=l+1;
l=fd(l+1);
}
}
for(ll i=1;i<=n;i++){
cout<<a[i]<<"\n";
}
return 0;
}
再次警示后人一下:for(ll i=1;i<=n+1;i++)fa[i]=i;,在这一句话中,我们通常是赋值到 \(n\),这里为什么是 \(n+1\) 呢,因为当你把 \(l\) 的 \(fa\) 数组指向 \(l+1\) 时,思考一下,当你的 \(l\) 如果等于 \(n\) 呢,他会指向 \(fa_{n+1}\),即为 \(0\),细细思考一下,会发现这十分的危险,因为这样会使你步入死循环。

浙公网安备 33010602011771号