BZOJ2959長跑——LCT+並查集(LCT動態維護邊雙連通分量)
題目描述
某校開展了同學們喜聞樂見的陽光長跑活動。為了能“為祖國健康工作五十年”,同學們紛紛離開寢室,離開教室,離開實驗室,到操場參加3000米長跑運動。一時間操場上熙熙攘攘,摩肩接踵,盛況空前。
為了讓同學們更好地監督自己,學校推行了刷卡機制。
學校中有n個地點,用1到n的整數表示,每個地點設有若干個刷卡機。
有以下三類事件:
1、修建了一條連線A地點和B地點的跑道。
2、A點的刷卡機臺數變為了B。
3、進行了一次長跑。問一個同學從A出發,最後到達B最多可以刷卡多少次。具體的要求如下:
當同學到達一個地點時,他可以在這裡的每一臺刷卡機上都刷卡。但每臺刷卡機只能刷卡一次,即使多次到達同一地點也不能多次刷卡。
為了安全起見,每條跑道都需要設定一個方向,這條跑道只能按照這個方向單向通行。最多的刷卡次數即為在任意設定跑道方向,按照任意路徑從A地點到B地點能刷卡的最多次數。
輸入
輸入的第一行包含兩個正整數n,m,表示地點的個數和操作的個數。
第二行包含n個非負整數,其中第i個數為第個地點最開始刷卡機的臺數。
接下來有m行,每行包含三個非負整數P,A,B,P為事件型別,A,B為事件的兩個引數。
最初所有地點之間都沒有跑道。
每行相鄰的兩個數之間均用一個空格隔開。表示地點編號的數均在1到n之間,每個地點的刷卡機臺數始終不超過10000,P=1,2,3。
輸出
輸出的行數等於第3類事件的個數,每行表示一個第3類事件。如果該情況下存在一種設定跑道方向的方案和路徑的方案,可以到達,則輸出最多可以刷卡的次數。如果A不能到達B,則輸出-1。
樣例輸入
9 3110 20 30 40 50 60 70 80 90
3 1 2
1 1 3
1 1 2
1 8 9
1 2 4
1 2 5
1 4 6
1 4 7
3 1 8
3 8 8
1 8 9
3 8 8
3 7 5
3 7 3
1 4 1
3 7 5
3 7 3
1 5 7
3 6 5
3 3 6
1 2 4
1 5 5
3 3 6
2 8 180
3 8 8
2 9 190
3 9 9
2 5 150
3 3 6
2 1 210
3 3 6
樣例輸出
-1-1
80
170
180
170
190
170
250
280
280
270
370
380
580
提示
資料規模及約定
對於100%的資料,m<=5n,任意時刻,每個地點的刷卡機臺數不超過10000。N<=1.5×105
可以發現對於每一個邊雙只要能走進這個邊雙那麼邊雙裡面所有點就都能到達。
將每個邊雙縮成一個點,用LCT來維護縮點後的樹。
因此連邊可以分成兩種情況討論:
1、當兩點聯通但不屬於同一個邊雙時,那麼加入這條邊之後這兩點及兩點之前路徑上的點組成了一個新的邊雙,在LCT上dfs這些路徑上的點將他們的大小合併到新形成的邊雙代表的點上並將這些點刪除。
2、當兩點不連通時,這時連線兩點不會出現新的邊雙,直接在LCT上連線兩點即可。
用兩個並查集,分別維護每個點屬於哪個邊雙及點與點之間的連通性。注意點之間連通性不能用LCT的find找(會TLE),而要用並查集。
因為每個點和邊最多隻會被刪除一次也就是隻會被dfs一次,所以時間複雜度是O((n+m)*logn)
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define pr pair<int,int> #define ll long long using namespace std; int g[150010]; int fa[150010]; int f[150010]; int s[150010][2]; int v[150010]; int sum[150010]; int size[150010]; int st[150010]; int r[150010]; int n,m; int opt; int x,y; int find(int x) { if(fa[x]==x) { return x; } return fa[x]=find(fa[x]); } int judge(int x) { if(g[x]==x) { return x; } return g[x]=judge(g[x]); } int is_root(int rt) { return rt!=s[find(f[rt])][0]&&rt!=s[find(f[rt])][1]; } int get(int rt) { return rt==s[find(f[rt])][1]; } void pushup(int rt) { sum[rt]=sum[s[rt][0]]+sum[s[rt][1]]+size[rt]; } void pushdown(int rt) { if(r[rt]) { swap(s[rt][0],s[rt][1]); r[s[rt][0]]^=1; r[s[rt][1]]^=1; r[rt]^=1; } } void rotate(int rt) { int fa=find(f[rt]); int anc=find(f[fa]); int k=get(rt); if(!is_root(fa)) { s[anc][get(fa)]=rt; } s[fa][k]=s[rt][k^1]; f[s[fa][k]]=fa; s[rt][k^1]=fa; f[fa]=rt; f[rt]=anc; pushup(fa); pushup(rt); } void splay(int rt) { int top=0; st[++top]=rt; for(int i=rt;!is_root(i);i=find(f[i])) { st[++top]=find(f[i]); } for(int i=top;i>=1;i--) { pushdown(st[i]); } for(int fa;!is_root(rt);rotate(rt)) { if(!is_root(fa=find(f[rt]))) { rotate(get(fa)==get(rt)?fa:rt); } } } void access(int rt) { for(int x=0;rt;x=rt,rt=find(f[rt])) { splay(rt); s[rt][1]=x; pushup(rt); } } void reverse(int rt) { access(rt); splay(rt); r[rt]^=1; } void link(int x,int y) { reverse(x); f[x]=y; } void cut(int x,int y) { reverse(x); access(y); splay(y); f[x]=s[y][0]=0; pushup(y); } void dfs(int x,int rt) { fa[x]=rt; if(s[x][0]) { dfs(s[x][0],rt); } if(s[x][1]) { dfs(s[x][1],rt); } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&v[i]); size[i]=v[i]; sum[i]=v[i]; fa[i]=i; g[i]=i; } while(m--) { scanf("%d%d%d",&opt,&x,&y); int fx=find(x); int fy=find(y); if(opt==1) { if(fx!=fy) { if(judge(fx)!=judge(fy)) { link(fx,fy); g[g[fx]]=g[fy]; } else { reverse(fx); access(fy); splay(fy); size[fy]=sum[fy]; dfs(fy,fy); s[fy][0]=0; } } } else if(opt==2) { splay(fx); size[fx]+=y-v[x]; sum[fx]+=y-v[x]; v[x]=y; } else { if(judge(fx)!=judge(fy)) { printf("-1\n"); } else { reverse(fx); access(fy); splay(fy); printf("%d\n",sum[fy]); } } } }