【BZOJ4538】網路(HNOI2016)-整體二分+樹上差分+樹狀陣列
阿新 • • 發佈:2018-12-10
測試地址:網路 做法: 本題需要用到整體二分+樹上差分+樹狀陣列。 各位大佬想的都是用一些樹鏈剖分+線段樹套堆這種詭異操作,卡進去的,然而像我這種常數爆大選手根本不敢寫…於是發現還有個的方法,還是挺神的。 考慮只有一個詢問的時候,二分答案,每次二分一個,要知道這個詢問的答案是不是大於,只要把前面那些大於的修改計算一下,看到這個詢問的時刻時,是不是所有存在的路徑都過這個點,如果不是就說明這個詢問答案大於,否則相反。 我們發現涉及到的操作和詢問都隨著二分的答案區間變化,於是很自然地想到整體二分。於是在判定的時候,用樹上差分轉化成單點修改子樹詢問,拍到DFS序上用樹狀陣列維護即可,時間複雜度為(為最大的答案),可以通過此題。 …然而現實非常骨感,如果每次用到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;
}