Bzoj4538:[Hnoi2016]网络:整体二分+树状数组

3 篇文章 0 订阅
1 篇文章 0 订阅

题目链接:4538:[Hnoi2016]网络

整体二分,用dfs序+树状数组判断当前答案是否可行

对于一段区间[l,r],我们二分得到的答案为mid,我们把[l,r]中大于mid的数据交互请求加入树状数组,同时也有撤销操作的删除

这样知道我们发现了一个询问操作,我们在树状数组中做子树查询,就可以知道当前点坏掉会影响多少条数据交互请求

设区间[l,r]中我们在当前操作的时候还存在有num个数据交互请求,我们子树查询得到的答案为ans,如果ans<num,说明存在没有受到影响的链,那么当前二分得到的答案mid就可以是这个询问的答案,换句话说当前询问的答案>=mid

#include<queue>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500010;
int n,m,tot=1,h[maxn],fa[maxn][20],dep[maxn];
struct edge{int to,next;}G[maxn*10];
struct ques{int u,v,w,id,b,opt,k,lca;}p[maxn];
int s[maxn],flag[maxn],tim=0,cle=0,st[maxn];
int ans[maxn],mx=0,tmp[maxn],en[maxn];

void add_edge(int x,int y){
	G[++tot].to=y;G[tot].next=h[x];h[x]=tot;
}

void dfs(int x,int f){
	st[x]=++tim;
	dep[x]=dep[f]+1;
	for (int i=1;i<=18;++i){
		if (dep[x]<(1<<i)) break;
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for (int i=h[x];i;i=G[i].next){
		int v=G[i].to;
		if (v==f) continue;
		fa[v][0]=x; dfs(v,x);
	}en[x]=tim;
}

int query(int a,int b){
    if (dep[a]<dep[b]) swap(a,b);
    int t=dep[a]-dep[b];
    for (int i=0;i<=18;++i)
       if (t&(1<<i)) a=fa[a][i];
	if (a==b) return a;
    for (int i=18;~i;i--)
       if (fa[a][i]!=fa[b][i])
           a=fa[a][i],b=fa[b][i];
    return fa[a][0];
}

int lowbit(int x){return x&-x;}

void add(int x,int v){
	for (int i=x;i<=n;i+=lowbit(i)){
		if (flag[i]!=cle) s[i]=0,flag[i]=cle;
		s[i]+=v;
	}
}

int ask(int x){
	int ret=0;
	for (int i=x;i>=1;i-=lowbit(i))
		if (flag[i]==cle) ret+=s[i];
	return ret;
}

bool cmp(const ques &a,const ques &b){
	return a.k<b.k||(a.k==b.k&&a.id<b.id);
}

void solve(int L,int R,int l,int r){
	int mid=(L+R)>>1;
	if (l>r) return;
	if (L==R){
		for (int i=l;i<=r;++i)
		    if (p[i].opt==2) ans[p[i].id]=L;
		return;
	}
	++cle; int t=l-1,num=0;
	for (int i=l;i<=r;++i){
	    if (p[i].opt==0){
			if (p[i].w>mid){
				p[i].k=1; int lca=p[i].lca; num++;
				add(st[p[i].u],1); add(st[p[i].v],1);
				add(st[lca],-1);
				if (fa[lca][0]) add(st[fa[lca][0]],-1);
			}else p[i].k=0,t++;
	    }else if (p[i].opt==1){
			if (p[i].w>mid){
				p[i].k=1; int lca=p[i].lca; num--;
				add(st[p[i].u],-1); add(st[p[i].v],-1);
				add(st[lca],1);
				if (fa[lca][0]) add(st[fa[lca][0]],1);
			}else p[i].k=0,t++;
	    }else{
			int ret=ask(en[p[i].b])-ask(st[p[i].b]-1);
			if (ret<num) p[i].k=1;
			else p[i].k=0,t++;
	    }
	}
	sort(p+l,p+r+1,cmp);
	solve(L,mid,l,t);
	solve(mid+1,R,t+1,r);
}

int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<n;++i){
		int x,y; scanf("%d%d",&x,&y);
		add_edge(x,y); add_edge(y,x);
	}
	dfs(1,0);
	for (int i=1;i<=m;++i){
		int opt,x,y,z; p[i].id=i;
		scanf("%d",&opt); p[i].opt=opt;
		if (!opt){
			scanf("%d%d%d",&x,&y,&z);
			p[i].u=x; p[i].v=y; p[i].w=z;
			p[i].lca=query(p[i].u,p[i].v);
			mx=max(mx,z);
		}else if (opt==1){
			scanf("%d",&x); p[i]=p[x];
			p[i].opt=1; p[i].id=i;
		}else if (opt==2)scanf("%d",&p[i].b);
		tmp[i]=opt;
	}
	solve(0,mx,1,m);
    for (int i=1;i<=m;++i)
		if (tmp[i]==2) printf("%d\n",ans[i]==0?-1:ans[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值