The 2017 ACM-ICPC Asia Beijing Regional Contest C - Graph
题面
题意
给出一张图,每次询问给出l,r,定义序号在l,r之间的点为安全的,求只经过安全点就可以互相到达的点对数。
做法
首先题目不难转化为求所有联通块的贡献和,每个联通块的贡献为,s表示该联通块的大小。
因为是联通块,所以我们考虑有哪些边在块内,然后每条边记录两条(u->v,v->u),根据左端点排序,这样序号在一段区间内的点所对应的边就是一段区间内出现两次的边(两个方向各出现一次)。
然后我们就可以用莫队+可撤销并查集来做,我们可以发现经过莫队的预处理后,如果询问的左右端点在同一个块内,则直接暴力求解,否则每一块中的询问情况应该如图:
每条横着的线段表示一个询问,然后发现左端点参差不齐,而右端点单调递增。
因此在处理询问时,可以先加入右边的边,然后再加左边的边,求出答案后再撤销左边的所有边,这样每次删掉的边就是刚加入的边,就能用带撤销并查集维护了。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first
#define se second
#define N 200010
using namespace std;
ll T,n,m,Q,s,ks,fa[N],size[N],ans,an[N],cnt[N],a[N],b[N],fl[N],fr[N],top,tail;
P sta[N];
struct Bn
{
ll u,v,id;
bool operator < (const Bn &t) const
{
return u<t.u;
}
}bn[N<<1];
struct Que
{
ll l,r,id;
bool operator < (const Que &u) const
{
if(fl[l]/s!=fl[u.l]/s) return fl[l]/s<fl[u.l]/s;
return fr[r]<fr[u.r];
}
}que[N];
inline ll calc(ll u){return u*(u-1)/2;}
ll ff(ll u){return u==fa[u]?u:ff(fa[u]);}
inline void mg(ll u,ll v)
{
if(size[u]>size[v]) swap(u,v);
sta[++top]=mp(u,v);
ans+=calc(size[u]+size[v])-calc(size[u])-calc(size[v]);
size[v]+=size[u];
fa[u]=v;
}
inline void spl(ll u,ll v)
{
size[v]-=size[u];
fa[u]=u;
ans-=calc(size[u]+size[v])-calc(size[u])-calc(size[v]);
}
inline void add(ll u)
{
cnt[bn[u].id]++;
ll p=ff(bn[u].u),q=ff(bn[u].v);
if(cnt[bn[u].id]==2&&p!=q)
{
mg(p,q);
}
}
inline void clear()
{
for(;top!=tail;top--)
{
spl(sta[top].fi,sta[top].se);
}
}
inline ll solve(ll u,ll v)
{
ll i,j,res;
for(i=u;i<=v;i++) add(i);
res=ans;
clear();
for(i=u;i<=v;i++) cnt[bn[i].id]--;
return res;
}
int main()
{
ll i,j,k,p,q,l,r;
cin>>T;
while(T--)
{
memset(fr,0,sizeof(fr));
scanf("%lld%lld%lld",&n,&m,&Q);
s=sqrt(m<<1),ks=(m<<1)/s;
for(i=1;i<=m;i++)
{
scanf("%lld%lld",&p,&q);
bn[(i<<1)-1].u=p;
bn[(i<<1)-1].v=q;
bn[(i<<1)-1].id=i;
bn[(i<<1)].u=q;
bn[(i<<1)].v=p;
bn[(i<<1)].id=i;
}
for(i=1;i<=n;i++) fl[i]=m*2+1;
m*=2;
sort(bn+1,bn+m+1);
for(i=1;i<=m;i++)
{
fl[bn[i].u]=min(fl[bn[i].u],i);
fr[bn[i].u]=max(fr[bn[i].u],i);
}
for(i=n-1;i>=1;i--) fl[i]=min(fl[i],fl[i+1]);
for(i=1;i<n;i++) fr[i+1]=max(fr[i+1],fr[i]);
for(i=1;i<=Q;i++)
{
scanf("%lld%lld",&que[i].l,&que[i].r);
que[i].id=i;
}
sort(que+1,que+Q+1);
for(i=0,k=1;i<=ks;i++)
{
for(j=1;j<=n;j++) fa[j]=j,size[j]=1;
l=(i+1)*s,r=l;
top=tail=0;
for(;k<=Q&&fl[que[k].l]/s==i;k++)
{
p=fl[que[k].l],q=fr[que[k].r];
if(q/s==i)
{
an[que[k].id]=solve(p,q);
continue;
}
for(;r<=q;r++) add(r);
tail=top;
for(j=l-1;j>=p;j--) add(j);
an[que[k].id]=ans;
for(j=l-1;j>=p;j--) cnt[bn[j].id]--;
clear();
}
for(j=r-1;j>=l;j--) cnt[bn[j].id]--;
tail=0;
clear();
}
for(i=1;i<=Q;i++) printf("%lld\n",an[i]);
}
}