CF487E Tourists 圓方樹、樹鏈剖分
阿新 • • 發佈:2019-01-18
傳送門 ret 如果 using 這一 define 一次 eof getch
傳送門
註意到我們需要求的是兩點之間所有簡單路徑中最小值的最小值,那麽對於一個點雙聯通分量來說,如果要經過它,則一定會經過這個點雙聯通分量裏權值最小的點
註意:這裏不能縮邊雙聯通分量,樣例\(2\)就是一個反例
上面這個圖如果縮點雙會縮成\(3\)個,但是縮邊雙會將整個圖縮成\(1\)個點。
假如我們詢問的是\((1,4)\)之間的簡單路徑,而圖中權值最小的點為\(7\)號點,那麽如果縮成了邊雙聯通分量,你的答案會是\(7\)號點的權值,意即認為可以走到\(7\)號點,但實際上如果到\(7\)號點,意味著\(5\)號點需要經過\(2\)次,不符合簡單路徑的要求
所以如果將題意改成“一條邊只能經過一次”就是縮邊雙了
那麽我們直接維護圓方樹,對於每一個方點使用\(multiset\)維護與它相連的所有圓點的權值,在圓方樹上樹鏈剖分計算答案。
當然這樣子還是不夠的。考慮一種情況:一個圓點連接了一堆方點,然後在這一個圓點上不斷進行修改操作,這樣每一次修改都會波及一大堆方點的修改,復雜度直接爆炸。
優化:對於所有方點,不去維護它在圓方樹上的父親,那麽對於每一次修改,只會波及它在圓方樹上的方點父親。而如果在某一次詢問中兩點之間的\(LCA\)為方點,還需要額外考慮這個點的父親的貢獻。
#include<bits/stdc++.h> #define lch (x << 1) #define rch (x << 1 | 1) #define mid ((l + r) >> 1) #define INF 0x7fffffff //This code is written by Itst using namespace std; inline int read(){ int a = 0; char c = getchar(); bool f = 0; while(!isdigit(c) && c != EOF){ if(c == ‘-‘) f = 1; c = getchar(); } if(c == EOF) exit(0); while(isdigit(c)){ a = a * 10 + c - 48; c = getchar(); } return f ? -a : a; } const int MAXN = 2e5 + 7; struct Edge{ int end , upEd; }Ed[MAXN << 1]; int head[MAXN] , val[MAXN] , N , M , Q , cntEd , cnt; int topS , ts , st[MAXN] , dfn[MAXN] , low[MAXN]; int ind[MAXN] , rk[MAXN] , fa[MAXN] , dep[MAXN] , son[MAXN] , sz[MAXN] , top[MAXN] , TS; int Tree[MAXN << 2]; bool vis[MAXN]; vector < int > ch[MAXN]; multiset < int > s[MAXN]; inline void addEd(int a , int b){ Ed[++cntEd].end = b; Ed[cntEd].upEd = head[a]; head[a] = cntEd; } inline void pop(int t , int bot){ ch[t].push_back(++cnt); do{ ch[cnt].push_back(st[topS]); s[cnt].insert(val[st[topS]]); }while(st[topS--] != bot); } void tarjan(int x , int p){ st[++topS] = x; dfn[x] = low[x] = ++ts; vis[x] = 1; for(int i = head[x] ; i ; i = Ed[i].upEd) if(Ed[i].end != p) if(!vis[Ed[i].end]){ tarjan(Ed[i].end , x); low[x] = min(low[x] , low[Ed[i].end]); if(low[Ed[i].end] >= dfn[x]) pop(x , Ed[i].end); } else low[x] = min(low[x] , dfn[Ed[i].end]); } void dfs1(int x , int p){ fa[x] = p; dep[x] = dep[p] + 1; sz[x] = 1; for(int i = 0 ; i < ch[x].size() ; ++i){ dfs1(ch[x][i] , x); sz[x] += sz[ch[x][i]]; if(sz[son[x]] < sz[ch[x][i]]) son[x] = ch[x][i]; } } void dfs2(int x , int t){ ind[x] = ++TS; rk[TS] = x; top[x] = t; if(!son[x]) return; dfs2(son[x] , t); for(int i = 0 ; i < ch[x].size() ; ++i) if(ch[x][i] != son[x]) dfs2(ch[x][i] , ch[x][i]); } inline void pushup(int x){ Tree[x] = min(Tree[lch] , Tree[rch]); } void init(int x , int l , int r){ if(l == r) Tree[x] = rk[l] <= N ? val[rk[l]] : *s[rk[l]].begin(); else{ init(lch , l , mid); init(rch , mid + 1 , r); pushup(x); } } void modify(int x , int l , int r , int tar , int num){ if(l == r) Tree[x] = num; else{ if(mid >= tar) modify(lch , l , mid , tar , num); else modify(rch , mid + 1 , r , tar , num); pushup(x); } } int query(int x , int l , int r , int L , int R){ if(l >= L && r <= R) return Tree[x]; int minN = INF; if(mid >= L) minN = min(minN , query(lch , l , mid , L , R)); if(mid < R) minN = min(minN , query(rch , mid + 1 , r , L , R)); return minN; } int work(int x , int y){ int tx = top[x] , ty = top[y] , minN = INF; while(tx != ty){ if(dep[tx] < dep[ty]){ swap(x , y); swap(tx , ty); } minN = min(minN , query(1 , 1 , cnt , ind[tx] , ind[x])); x = fa[tx]; tx = top[x]; } if(dep[x] > dep[y]) swap(x , y); minN = min(minN , query(1 , 1 , cnt , ind[x] , ind[y])); if(x > N) minN = min(minN , val[fa[x]]); return minN; } inline char getc(){ char c = getchar(); while(!isupper(c)) c = getchar(); return c; } int main(){ #ifndef ONLINE_JUDGE freopen("in","r",stdin); //freopen("out","w",stdout); #endif cnt = N = read(); M = read(); Q = read(); for(int i = 1 ; i <= N ; ++i) val[i] = read(); for(int i = 1 ; i <= M ; ++i){ int a = read() , b = read(); addEd(a , b); addEd(b , a); } tarjan(1 , 0); dfs1(1 , 0); dfs2(1 , 1); init(1 , 1 , cnt); while(Q--) if(getc() == ‘A‘) printf("%d\n" , work(read() , read())); else{ int a = read() , b = read(); if(a != 1){ s[fa[a]].erase(s[fa[a]].find(val[a])); s[fa[a]].insert(b); modify(1 , 1 , cnt , ind[fa[a]] , *s[fa[a]].begin()); } val[a] = b; modify(1 , 1 , cnt , ind[a] , b); } return 0; }
CF487E Tourists 圓方樹、樹鏈剖分