题解: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;
}
posted @ 2025-08-11 10:50  TBSF_0207  阅读(7)  评论(0)    收藏  举报