Description
众所周知,Zjr506是算法之神,因此Ztxz16经常向他请教算法。这一天,Zjr506在教导了Ztxz16关于图论方面的一些算法后,给他出了一道图论题作为家庭作业:
给定N个点,M条无向边,Q个询问,每个询问给定L, R,问连上第L~R条边后,图中有多少联通块(询问之间互不影响)。
Ztxz16智商太低,百思不得其解,只好向你请教这个问题。
Solution
用LCT做的
把模型转化一下。第i条边的权值是i,每次不断的加边,维护一个最大生成树。可以用LCT来维护删边和加边,最后树的个数就是联通块的数量。为什么是最大生成树,因为这样更方便删边和加边。
那么我们可以处理出ans数组,ans[i]表述1~i的边都添加后的答案。
同时我们还要维护n棵主席树为权值线段树,一条边若对ans[i]有贡献,则在第i棵主席树的值为1。
那么对于一个询问l和r,则答案为ans[r]+第r棵主席树在区间1~l-1的权值和。
莫队算法
如果只有加边操作的话,会很好做。那么我们就把所有的删边操作变成加边操作。
把[ll,rr]的询问,l先到l所在块的最右端,然后再添加[ll,l],做完之后再还原,用data[i]表示i原来的父亲,用于还原。
注意,r是逐个递增的,当l的块变动的时候,就暴力重构。
每次操作之后上一层的f都要还原。r的变动时,要修改data。
细节比较多,防止出错,把l,r同块的情况分出来了。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200007;
int i,j,k,l,t,n,m,ans;
int kuai[maxn],data[maxn],head,tail,da,r,num;
int b[maxn],c[maxn],ans1[maxn],f[maxn],ans2,ans3;
int bz[maxn],bb;
struct node{
int a,b,c;
}a[maxn],d[maxn];
bool cmp(node x,node y){
return kuai[x.a]<kuai[y.a]||kuai[x.a]==kuai[y.a]&&x.b<y.b;
}
bool cmp1(node x,node y){
return x.a<y.a||x.a==y.a&&x.b<y.b;
}
int gf(int x){
if(bz[x]!=bb){
bz[x]=bb;
f[x]=data[x];
}
if(f[x]==x)return x;
f[x]=gf(f[x]);
return f[x];
}
int gf1(int x){
if(bz[x]!=bb){
bz[x]=bb;
f[x]=data[x];
}
if(f[x]==x)return x;
data[x]=f[x]=gf(f[x]);
return f[x];
}
void jin(int x){
int u=gf(b[x]),v=gf(c[x]);
if(u!=v)ans--,f[v]=u;
}
void jin1(int x){
int u=gf1(b[x]),v=gf1(c[x]);
if(u!=v)ans--,f[v]=data[v]=u;
}
int main(){
scanf("%d%d%d",&n,&m,&k);
da=100;
fo(i,1,n)kuai[i]=(i-1)/da+1;
fo(i,1,m){
scanf("%d%d",&b[i],&c[i]);
}
fo(i,1,k){
scanf("%d%d",&a[i].a,&a[i].b);
a[i].c=i;
}
sort(a+1,a+1+k,cmp);
l=0;
ans=n;
fo(i,1,n)f[i]=i;
fo(j,1,k){
if(a[j].b<kuai[a[j].a]*da){
d[++num]=a[j];
continue;
}
if(kuai[l]!=kuai[a[j].a]){
fo(i,1,n){
f[i]=i,bz[i]=bb;
}
ans=n;
l=kuai[a[j].a]*da,r=a[j].b;
fo(i,l,r)jin(i);
fo(i,1,n)data[i]=f[i];
ans2=ans;
}
bb++;l=kuai[a[j].a]*da;ans=ans2;
while(r<a[j].b)jin1(++r);ans2=ans;
while(l>a[j].a)jin(--l);
ans1[a[j].c]=ans;
}
sort(d+1,d+1+num,cmp1);
l=r=0;ans=n;
fo(i,1,n)data[i]=i;
fo(j,1,num){
if(l<d[j].a){
bb++,ans=n;
fo(i,d[j].a,d[j].b)jin(i);
l=d[j].a,r=d[j].b;
}
while(r<d[j].b)jin(++r);
ans1[d[j].c]=ans;
}
fo(i,1,k)printf("%d\n",ans1[i]);
}