题解:P10268 符卡对决
这里主要是对可撤销并查集和回滚莫队一些细节的详细解析。
注意到加入关系相当于记录在无向图图中的联通块,容易用并查集维护,而删除关系难以直接维护。这时我们可以使用只增不减莫队,令块长为 \(b\),对于左端点在同一块内的询问,我们先推右端点,保存,再推左端点,记录答案,然后回退到存档点,准备下一次操作。对于不同块内的询问,每次到该块时全体初始化即可。如果左右端点在同一块内,直接暴力做即可。
这里我们的答案是联通块内所有有序数对 \((i,j)\) 的 \(a_i\times a_j\) 的和再 \(\times \frac{1}{n(n-1)}\),所以对于并查集的一个节点需要维护 \(\sum a_i\)。因为要撤销,使用按秩合并需要记录 \(siz_i\) 作为子树大小,用一个栈来记录修改节点,方便撤销。
对于 \(\frac{m}{b}\) 个块,每个块右端点右推 \(O(m)\),每次并查集操作 \(O(\log n)\)。对于 \(q\) 个询问,每次左端点左推和撤销 \(O(b)\),并查集操作 \(O(\log n)\),总的时间复杂度为 \(O(\log n(\frac{m^2}{b}+qb))\)。根据基本不等式知识,块长设为 \(\sqrt{\frac{m^2}{q}}\) 取到最小,在 \(q,m\) 同阶时,取到 \(\sqrt{m}\) 即可。
具体实现细节参见代码。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int N=2e5+5;
const LL MOD=1e9+7;
int n,m,rq,a[N];
int U[N],V[N];
struct uni{
PII stk[N];
int tp,fa[N],siz[N];
LL sum[N];
bool rcd;
void init(){for(int i=1;i<=n;i++)fa[i]=i,sum[i]=a[i],siz[i]=1;}
void sinit(int x){fa[x]=x,sum[x]=a[x],siz[x]=1;}
void op(){rcd=1;tp=0;}
inline int fr(int x){
if(fa[x]==x)return x;
return fr(fa[x]);
}
void undo(){
while(tp){
PII a=stk[tp--];
int x=a.first,y=a.second;
fa[x]=x;sum[y]-=sum[x];
siz[y]-=siz[x];
}
rcd=0;
}
LL ins(int x,int y){
int frx=fr(x),fry=fr(y);
if(frx==fry)return 0;
if(siz[frx]>siz[fry])swap(frx,fry);
if(rcd)stk[++tp]=make_pair(frx,fry);
fa[frx]=fry;
sum[fry]+=sum[frx];
siz[fry]+=siz[frx];
return 1ll*((sum[fry]-sum[frx])%MOD)*(sum[frx]%MOD)%MOD;
}
}un;
LL temp,ans[N];
int bs,b[N];
struct Q{int l,r,id;}q[N];
bool cmp(Q x,Q y){
return (b[x.l]!=b[y.l]?b[x.l]<b[y.l]:x.r<y.r);
}
inline void add(int x){
LL p=un.ins(U[x],V[x]);
temp=(temp+p)%MOD;
}
LL qkpow(LL x,LL y){
LL res=1;
while(y){
if(y&1)res=res*x%MOD;
x=x*x%MOD;
y>>=1;
}
return res;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m>>rq;bs=sqrt(m);
LL aps=qkpow(1ll*n*(n-1)%MOD,MOD-2);
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=m;i++)
cin>>U[i]>>V[i],
b[i]=(i-1)/bs+1;
for(int i=1;i<=rq;i++){
cin>>q[i].l>>q[i].r;
q[i].id=i;
}
sort(q+1,q+1+rq,cmp);
int l=-1,r=-2;
for(int i=1;i<=rq;i++){
if(l<q[i].l){
un.init();
temp=0;
l=min(b[q[i].l]*bs,m);
r=l-1;
}
if(q[i].r<r){
un.op();
for(int j=q[i].l;j<=q[i].r;j++)
add(j);
ans[q[i].id]=temp*2%MOD;
un.undo();
temp=0;
continue;
}
while(r<q[i].r)add(++r);
LL dt=temp;
int orl=l;
un.op();
while(l>q[i].l)add(--l);
ans[q[i].id]=temp*2%MOD;//*2因为是有序数对
un.undo();
temp=dt;
l=orl;
}
for(int i=1;i<=rq;i++)
cout<<ans[i]*aps%MOD<<'\n';
return 0;
}

浙公网安备 33010602011771号