BZOJ3514 GERALD07加強版
阿新 • • 發佈:2018-12-21
Link
Difficulty
演算法難度7,思維難度7,程式碼難度7
Description
給定一個無向圖,有個點,條邊,邊從到標號
次詢問,每次詢問內的邊組成的圖的連通塊個數
Solution
這題聽說可以樹套樹,但是會TLE
以下都是神仙做法:
我們維護按照編號加邊,維護邊的最大生成樹,邊的權值就是編號
每次如果沒有形成環就直接加,如果有環就替換掉最小的那條邊,可以LCT維護
我們記第條邊替換的邊的序號為,特別的,自環的,直接加的邊的
查詢的時候直接查內的個數,答案就是減去這個個數,可以主席樹維護
接下來我們嘗試證明這個結論的正確性:
我們考慮如果一條位於內的邊的取不同的值時,它對答案的貢獻
-
當時,也就是可以直接加上這條邊,那麼連通塊個數會-1
-
當時,也就是它替換掉了前面出現過的一條邊,並且這條邊的編號
你可能會說:“它沒有貢獻啊,它明明替換了一條邊,怎麼會有貢獻呢?”
但是你會發現它替換的邊的編號,那麼這時這條邊已經不存在了
也就是說現在這條邊就可以替代它的作用了,這時它就可以有1的貢獻了
-
當時,也就是說它替換了一條前面出現的邊,並且這條邊的編號
和前面同理,只不過這時它替換的邊是存在的,那麼現在這條邊就沒有貢獻了
-
當時,也就是說這條邊是一個自環,那麼它一定不會有貢獻
這樣我們就求得了內的邊對答案的貢獻了,就可以計算答案了
時間複雜度,本人在bzoj上跑了32s,寫起來應該沒什麼常數問題
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
inline int read(){
int x=0,f=1;char ch=' ';
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f==1?x:-x;
}
const int N=4e5+5;
namespace LCT{
int ch[N][2],f[N],rev[N],mn[N],id[N];
inline bool get(int x){return ch[f[x]][1]==x;}
inline void pushup(int x){mn[x]=min(id[x],min(mn[ch[x][0]],mn[ch[x][1]]));}
inline bool nroot(int x){return ch[f[x]][0]==x || ch[f[x]][1]==x;}
inline void reverse(int x){rev[x]^=1;swap(ch[x][0],ch[x][1]);}
inline void pushdown(int x){
if(rev[x]){
if(ch[x][0])reverse(ch[x][0]);
if(ch[x][1])reverse(ch[x][1]);
rev[x]=0;
}
}
inline void pd(int x){if(nroot(x))pd(f[x]);pushdown(x);}
inline void rotate(int x){
int y=f[x],z=f[y],w=get(x);
f[x]=z;if(nroot(y))ch[z][get(y)]=x;
ch[y][w]=ch[x][w^1];f[ch[y][w]]=y;
ch[x][w^1]=y;f[y]=x;
pushup(y);pushup(x);
}
inline void Splay(int x){
pd(x);
for(int fa=f[x];nroot(x);rotate(x),fa=f[x])
if(nroot(fa))
rotate((get(fa)==get(x))?fa:x);
}
inline void access(int x){for(int y=0;x;x=f[y=x])Splay(x),ch[x][1]=y,pushup(x);}
inline void makeroot(int x){access(x);Splay(x);reverse(x);}
inline int findroot(int x){access(x);Splay(x);while(ch[x][0])x=ch[x][0];return x;}
inline void split(int x,int y){makeroot(x);access(y);Splay(y);}
inline void link(int x,int y){split(x,y);f[x]=y;}
inline void cut(int x,int y){split(x,y);f[x]=ch[y][0]=0;}
};
using namespace LCT;
int n,m,q;
int X[N],Y[N],ntr[N];
int sum[N*30],ls[N*30],rs[N*30],sz,root[N];
inline void insert(int &now,int pre,int l,int r,int pos){
now=++sz;
sum[now]=sum[pre]+1;
ls[now]=ls[pre];
rs[now]=rs[pre];
if(l==r)return;
int mid=(l+r)>>1;
if(pos<=mid)insert(ls[now],ls[pre],l,mid,pos);
else insert(rs[now],rs[pre],mid+1,r,pos);
}
inline int query(int now,int pre,int l,int r,int pos){
if(l==r)return sum[now]-sum[pre];
int mid=(l+r)>>1;
if(pos<=mid)return query(ls[now],ls[pre],l,mid,pos);
else return query(rs[now],rs[pre],mid+1,r,pos)+sum[ls[now]]-sum[ls[pre]];
}
int main(){
memset(mn,0x3f,sizeof mn);
memset(id,0x3f,sizeof id);
n=read();m=read();q=read();int type=read();
for(int i=1;i<=m;++i){
X[i]=read();
Y[i]=read();
mn[i+n]=id[i+n]=i;
if(X[i]==Y[i]){
ntr[i]=m;
insert(root[i],root[i-1],0,m,ntr[i]);
continue;
}
int fx=findroot(X[i]),fy=findroot(Y[i]);
if(fx!=fy){
link(i+n,X[i]);
link(i+n,Y[i]);
ntr[i]=0;
}
else{
split(X[i],Y[i]);
int id=mn[Y[i]];
cut(id+n,X[id]);
cut(id+n,Y[id]);
link(i+n,X[i]);
link(i+n,Y[i]);
ntr[i]=id;
}
insert(root[i],root[i-1],0,m,ntr[i]);
}
int lastans=0;
for(int i=1;i<=q;++i){
int l=read()^lastans,r=read()^lastans;
printf("%d\n",lastans=n-query(root[r],root[l-1],0,m,l-1));
if(!type)lastans=0;
}
return 0;
}