1. 程式人生 > >【BZOJ4538】網路(HNOI2016)-整體二分+樹上差分+樹狀陣列

【BZOJ4538】網路(HNOI2016)-整體二分+樹上差分+樹狀陣列

測試地址:網路 做法: 本題需要用到整體二分+樹上差分+樹狀陣列。 各位大佬想的都是用一些樹鏈剖分+線段樹套堆這種詭異操作,O(mlog3n)O(m\log^3n)卡進去的,然而像我這種常數爆大選手根本不敢寫…於是發現還有22log\log的方法,還是挺神的。 考慮只有一個詢問的時候,二分答案,每次二分一個midmid,要知道這個詢問的答案是不是大於midmid,只要把前面那些大於midmid的修改計算一下,看到這個詢問的時刻時,是不是所有存在的路徑都過這個點,如果不是就說明這個詢問答案大於midmid,否則相反。 我們發現涉及到的操作和詢問都隨著二分的答案區間變化,於是很自然地想到整體二分。於是在判定的時候,用樹上差分轉化成單點修改子樹詢問,拍到DFS序上用樹狀陣列維護即可,時間複雜度為O

(mlognlogMAX)O(m\log n\log MAX)MAXMAX為最大的答案),可以通過此題。 …然而現實非常骨感,如果每次用到LCA都倍增算一次的話,就會被卡爆,然而Tarjan又難寫,為原已近200行的程式碼又貢獻幾十行,但我們發現要求的都是詢問中路徑的LCA,所以一開始處理好就可以卡進去了。 以下是本人程式碼:

#include <bits/stdc++.h>
using namespace std;
int n,m,first[100010]={0},tot=0;
int dep[100010]={0},fa[100010][20]={0},in[100010],out[
100010],tim=0; int LCA[200010],id[200010],tmpl[200010],tmpr[200010]; int ans[200010]={0},sum[100010]={0},cnt; struct edge { int v,next; }e[200010]; struct oper { int op,a,b,v; }q[200010]; int read() { char c; int s=0; c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') s=s*10+c-'0',c=getchar(); return
s; } void insert(int a,int b) { e[++tot].v=b; e[tot].next=first[a]; first[a]=tot; } void dfs(int v) { in[v]=++tim; for(int i=first[v];i;i=e[i].next) if (e[i].v!=fa[v][0]) { dep[e[i].v]=dep[v]+1; fa[e[i].v][0]=v; dfs(e[i].v); } out[v]=tim; } int lowbit(int x) { return x&(-x); } void add(int x,int d) { for(int i=x;i<=n;i+=lowbit(i)) sum[i]+=d; } int calc_sum(int x) { int ans=0; for(int i=x;i;i-=lowbit(i)) ans+=sum[i]; return ans; } int lca(int a,int b) { if (dep[a]<dep[b]) swap(a,b); for(int i=17;i>=0;i--) if (dep[fa[a][i]]>=dep[b]) a=fa[a][i]; if (a==b) return a; for(int i=17;i>=0;i--) if (fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i]; return fa[a][0]; } void modify(int x,int d) { int g=LCA[x],a=q[x].a,b=q[x].b; add(in[a],d),add(in[b],d),add(in[g],-d); if (fa[g][0]) add(in[fa[g][0]],-d); } void solve(int l,int r,int ql,int qr) { if (ql>qr) return; if (l==r) { if (l==0) { cnt=0; for(int i=ql;i<=qr;i++) { if (q[id[i]].op==0) cnt++,modify(id[i],1); if (q[id[i]].op==1) cnt--,modify(id[i],-1); if (q[id[i]].op==2) { int now=calc_sum(out[q[id[i]].a])-calc_sum(in[q[id[i]].a]-1); if (now<cnt) ans[id[i]]=0; else ans[id[i]]=-1; } } for(int i=ql;i<=qr;i++) { if (q[id[i]].op==0) modify(id[i],-1); if (q[id[i]].op==1) modify(id[i],1); } } else { for(int i=ql;i<=qr;i++) if (q[id[i]].op==2) ans[id[i]]=l; } return; } int mid=(l+r)>>1,tl=0,tr=0; cnt=0; for(int i=ql;i<=qr;i++) { if (q[id[i]].op==0) { if (q[id[i]].v>mid) cnt++,modify(id[i],1),tmpr[++tr]=id[i]; else tmpl[++tl]=id[i]; } if (q[id[i]].op==1) { if (q[id[i]].v>mid) cnt--,modify(id[i],-1),tmpr[++tr]=id[i]; else tmpl[++tl]=id[i]; } if (q[id[i]].op==2) { int now=calc_sum(out[q[id[i]].a])-calc_sum(in[q[id[i]].a]-1); if (now<cnt) tmpr[++tr]=id[i]; else tmpl[++tl]=id[i]; } } for(int i=ql;i<=qr;i++) { if (q[id[i]].op==0&&q[id[i]].v>mid) modify(id[i],-1); if (q[id[i]].op==1&&q[id[i]].v>mid) modify(id[i],1); } for(int i=1;i<=tl;i++) id[ql+i-1]=tmpl[i]; for(int i=1;i<=tr;i++) id[ql+tl+i-1]=tmpr[i]; solve(l,mid,ql,ql+tl-1); solve(mid+1,r,ql+tl,qr); } int main() { n=read(),m=read(); for(int i=1;i<n;i++) { int a,b; a=read(),b=read(); insert(a,b),insert(b,a); } dep[0]=-1; dfs(1); for(int i=1;i<=17;i++) for(int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1]; int mxv=0; for(int i=1;i<=m;i++) { q[i].op=read(); id[i]=i; if (q[i].op==0) { q[i].a=read(),q[i].b=read(),q[i].v=read(); mxv=max(mxv,q[i].v); LCA[i]=lca(q[i].a,q[i].b); } if (q[i].op==1) { int t; t=read(); q[i].a=q[t].a,q[i].b=q[t].b,q[i].v=q[t].v; LCA[i]=LCA[t]; } if (q[i].op==2) q[i].a=read(); } solve(0,mxv,1,m); for(int i=1;i<=m;i++) if (q[i].op==2) printf("%d\n",ans[i]); return 0; }