P5163 WD與地圖——整體二分
阿新 • • 發佈:2021-09-28
整體二分一類題目中的高階題
如果不是有向圖,這個題也就是個紫題水平。直接倒序加邊,每個點維護一棵權值線段樹,再用並查集判聯通塊即可……
但是,它是有向圖……
這意味著我們根本不知道什麼時候他會構成連通塊。
考慮每個有向邊,對一條邊連線的兩個點來說,肯定有一個時刻它們不再在同一個連通塊裡,所以單個邊考慮的話,只需要對序列二分判強連通就知道從什麼時候開始它們不在同一個連通塊裡。
然後發現了單個可以二分之後,就可以聯想到對序列整體進行二分來求出每一個邊的分離時間。
之後求出每個分離時間之後,再按正常套路倒序加邊,並查集維護連通塊,線段樹合併維護強連通第k大即可
(沒想到我人生中第一道線段樹合併是這個題……
detail:
這個題的細節巨多,能噁心死你
尤其是整體二分那塊真的是看到就噁心
還有,權值線段樹開成值域範圍,不要像我一樣開成n的大小……
線段樹合併用指標真能把人氣死 , 一個個特判看著就煩
#include<bits/stdc++.h> using namespace std; #define int long long #define INF 1ll<<30 #define pb push_back #define pii pair<int ,int > const int p=2e6+5; template<typename _T> inline void read(_T &x) { x=0;char s=getchar();int f=1; while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();} while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();} x*=f; } struct Edge{ int u,v,tim; }e[p]; map<pii,int> mapp; int a[p],b[p*4] , bili; int n,m,q; struct node{ int opt , a, b , tim; }c[p*4]; struct query{ int opt , a , b; }qa[p]; struct segment{ struct tree{ int l,r; tree *ls , *rs; int cnt , sum; inline void pushup(){cnt = ls->cnt + rs->cnt,sum = ls->sum + rs->sum;} inline int query(int k) { if(!this) return 0; if(cnt < k) return sum; if(l == r) return k*b[l]; if(rs && k<=rs->cnt) return rs->query(k); else return (rs?rs->sum:0 )+ ls->query(k - (rs?rs->cnt:0)); } }mem[p * 4],*pool = mem; tree *rot[p]; inline tree *New(int L,int R){++pool;pool->l = L;pool->r = R;return pool;} inline void update(tree *u,int pos,int add) { u->cnt+=add; u->sum += add*b[pos]; if(u->l == u->r) return; int mid = u->l + u->r >> 1; int L = u->l , R = u->r; if(pos <= mid) update((u->ls)?u->ls:(u->ls = New(L,mid)) , pos , add); else update(u->rs?u->rs:(u->rs = New(mid+1,R)) , pos , add); } inline void Merge(tree *a, tree *b) { if(!b) return; else if(!a->sum&&!a->cnt){*a = *b;return;} if(a->l == a->r) {a->cnt += b->cnt , a->sum += b->sum ; return;} else { int L = a->l , R = a->r , mid = L+R>>1; Merge(a->ls?a->ls:a->ls = New(L,mid),b->ls); Merge(a->rs?a->rs:a->rs = New(mid+1,R) , b->rs); if(a->ls&&a->rs) a->pushup(); else{ if(a->ls) a->cnt = a->ls->cnt , a->sum = a->ls->sum; else a->cnt = a->rs->cnt, a->sum = a->rs->sum; } } } inline void merge(int x,int y) {Merge(rot[x] , rot[y]);} }segT; struct bcj{ Edge st[p]; int top; int fa[p] , dep[p]; inline void Init(){for(int i=1;i<=n;i++) fa[i] = i , dep[i] = 1;} inline int f(int x){while(x!=fa[x])x = fa[x];return fa[x];} inline void unite(int x,int y) { x = f(x) , y = f(y); if(x == y) return; else { if(dep[x] < dep[y]) swap(x , y); fa[y] = x; st[++top] = {x , y ,dep[x] == dep[y]}; dep[x] += dep[y]==dep[x]; } } inline void Del(int lim) { while(top>lim) { int u = st[top].u,v = st[top].v; int add = st[top].tim; fa[v] = v; dep[u] -= add; top--; } } inline void Merge(int x,int y) { x = f(x) , y = f(y); if(x == y) return; else { if(dep[x] < dep[y]) swap(x , y); fa[y] = x; dep[x] += dep[y]==dep[x]; segT.merge(x , y); } } }s,s1; struct shp{ int dfn[p] , low[p]; int head[p],nxt[p],ver[p],tit; int top , st[p] , lim,vis[p]; inline void add(int x,int y) { ver[++tit] = y; nxt[tit] = head[x]; head[x] = tit; } inline void tarjan(int x) { dfn[x] = low[x] = ++lim; vis[x] = 1;st[++top] = x; for(int i=head[x] ;i;i =nxt[i]) { int v = ver[i]; if(!dfn[v]) tarjan(v) , low[x] = min(low[x] , low[v]); else if(vis[v]) low[x] = min(dfn[v] , low[x]); } if(dfn[x] == low[x]) { int now = st[top--]; vis[now] = 0; while(st[top + 1]!=x) { vis[st[top]] = 0; s.unite(now , st[top--]); } } } inline void solve(vector<int> x){for(auto i:x)if(!dfn[i]) tarjan(i);} inline void clear(vector<int> x){for(auto i:x) head[i] = low[i] = dfn[i] = 0;lim = tit = 0;} }Tarjan; Edge lef[p],rig[p]; int Tl,Tr; vector<int> vec; inline void solve(int vl,int vr,int l,int r) { if(l>r) return; if(vl == vr) { for(int i=l;i<=r;i++) e[i].tim = vl; return; } int mid = vl + vr >>1; vec.clear(); int lim = s.top; for(int i=l;i<=r;i++) { int u = e[i].u , v = e[i].v; int r1 = s.f(u) , r2 = s.f(v); int tim = e[i].tim; if(tim <= mid) Tarjan.add(r1,r2),vec.pb(r1) , vec.pb(r2); } Tarjan.solve(vec); Tl = Tr = 0; for(int i=l;i<=r;i++) { int u = e[i].u , v = e[i].v; int tim = e[i].tim; int r1 = s.f(u) , r2 = s.f(v); if(r1 != r2 || tim > mid) rig[++Tr] = e[i]; else lef[++Tl] = e[i]; } for(int i=l;i<=l+Tl - 1;i++) { e[i] = lef[i - l + 1]; } for(int i=l+Tl;i<=r;i++) { e[i] = rig[i - (l + Tl - 1)]; } Tarjan.clear(vec);int cop = Tl; solve(mid+1,vr,Tl + l , r); s.Del(lim); solve(vl,mid,l,l+cop-1); } int ans[p]; bitset<p> bit; int *en ; inline int True(int x) { return lower_bound(b+1 ,en , x) - b; } signed main() { read(n); read(m); read(q); for(int i=1;i<=n;i++) read(a[i]) , b[++bili] = a[i]; for(int i=1;i<=m;i++) { read(e[i].u); read(e[i].v); e[i].tim = 0; mapp[{e[i].u ,e[i].v}] = i; } for(int i=1;i<=q;i++) { read(qa[i].opt);read(qa[i].a);read(qa[i].b); if(qa[i].opt == 1) e[mapp[{qa[i].a,qa[i].b}]].tim = q - i +1; if(qa[i].opt == 2) b[++bili] = (a[qa[i].a] += qa[i].b); if(qa[i].opt == 3) bit[i] = 1; } s.Init(); solve(0 , q+1 , 1 , m); sort(b+1,b+1+bili); en = unique(b+1,b+1+bili); int End = en - b - 1; for(int i=1;i<=m;i++) c[i] = (node){1 , e[i].u , e[i].v , e[i].tim}; int Ti = m; for(int i=1;i<=q;i++) { if(qa[i].opt!= 1) c[++Ti] = (node){qa[i].opt , qa[i].a , qa[i].b , q - i + 1}; } sort(c+1,c+1+Ti , [&](node A,node B){return A.tim < B.tim;}); s1.Init(); for(int i=1;i<=n;i++){ segT.update(segT.rot[i]=segT.New(1,End), True(a[i]) , 1); } for(int i=1;i<=Ti;i++) { if(c[i].opt == 1) s1.Merge(s1.f(c[i].a) ,s1.f( c[i].b)); if(c[i].opt == 2) { int ftop = s1.f(c[i].a); segT.update(segT.rot[ftop],True(a[c[i].a]) , -1); a[c[i].a] -= c[i].b; segT.update(segT.rot[ftop] , True(a[c[i].a]) , 1); } if(c[i].opt == 3) { int ftop = s1.f(c[i].a); ans[q - c[i].tim + 1] = segT.rot[ftop]->query(c[i].b); } } for(int i=1;i<= q;i++) if(bit[i]) printf("%lld\n",ans[i]); return 0; }
在役一年我就沒寫過封裝的這麼嚴重的題 , 程式碼難度真是能和維護數列平分秋風
這個可以說是整體二分裡面的天花板之一了