【BZOJ4538】网络(HNOI2016)-整体二分+树上差分+树状数组
测试地址:网络
做法: 本题需要用到整体二分+树上差分+树状数组。
各位大佬想的都是用一些树链剖分+线段树套堆这种诡异操作,卡进去的,然而像我这种常数爆大选手根本不敢写…于是发现还有个的方法,还是挺神的。
考虑只有一个询问的时候,二分答案,每次二分一个,要知道这个询问的答案是不是大于,只要把前面那些大于的修改计算一下,看到这个询问的时刻时,是不是所有存在的路径都过这个点,如果不是就说明这个询问答案大于,否则相反。
我们发现涉及到的操作和询问都随着二分的答案区间变化,于是很自然地想到整体二分。于是在判定的时候,用树上差分转化成单点修改子树询问,拍到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;
}