#整体二分,树状数组#洛谷 3250 JZOJ 4450 网络

本文探讨了树状数组结合整体二分算法在树结构数据上的高效查询与更新策略,通过预处理LCA(最近公共祖先)问题,实现了对树上特定路径的快速操作。文章详细介绍了如何利用树状数组进行树上差分,并针对不同类型的查询操作,设计了一种基于重要度的区间划分策略,最终达到了O(qlog^2n)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目


分析

考虑整体二分,对于操作0在树上差分,用树状数组实现,预处理 q q q个询问的 Lca \text{Lca} Lca,然后对于操作2,若满足当前修改操作都符合子树里的增加,那么放在左边,否则放在右边;对于操作0、1,若重要度小于等于 m i d mid mid,选择左区间,否则树上差分并选择右区间,但是最后修改操作要撤销,时间复杂度 O ( q l o g 2 n ) O(qlog^2n) O(qlog2n)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=100101;
struct rec{int bel,rk,now,ans;}Q[N<<1],Ql[N<<1],Qr[N<<1];struct node{int y,next;}e[N<<1];
int k=1,n,m,ls[N],root,c[N],Lca[N<<1],mx,xx[N<<1],yy[N<<1],zz[N<<1],q,fat[N],dep[N],son[N],dfn[N],top[N],big[N],tot;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
inline signed max(int a,int b){return a>b?a:b;}
inline void dfs1(int x,int fa){
	dep[x]=dep[fa]+1,fat[x]=fa,son[x]=1;
	for (rr int i=ls[x],mson=-1;i;i=e[i].next)
	if (e[i].y!=fa){
		dfs1(e[i].y,x);
		son[x]+=son[e[i].y];
		if (son[e[i].y]>mson) big[x]=e[i].y,mson=son[e[i].y];
	}
}
inline void dfs2(int x,int linp){
	dfn[x]=++tot,top[x]=linp;
	if (!big[x]) return; dfs2(big[x],linp);
	for (rr int i=ls[x];i;i=e[i].next)
	if (e[i].y!=fat[x]&&e[i].y!=big[x])
	    dfs2(e[i].y,e[i].y);
}
inline signed lca(int x,int y){
	while (top[x]!=top[y]){
		if (dep[top[x]]<dep[top[y]]) x^=y,y^=x,x^=y;
		x=fat[top[x]];
	}
	if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
	return x;
}
bool cmp(rec x,rec y){return x.rk<y.rk;}
inline signed query(int x){rr int ans=0; for (;x;x-=-x&x) ans+=c[x]; return ans;}
inline void add(int x,int y){for (;x<=n;x+=-x&x) c[x]+=y;}
inline void Update(int x,int y,int z,int zlca){
    add(dfn[x],z),add(dfn[y],z),add(dfn[zlca],-z);
    if (fat[zlca]) add(dfn[fat[zlca]],-z);
}
inline void update(int L,int R,int l,int r){
    if (L==R){
        for (rr int i=l;i<=r;++i)
            if (Q[i].bel==2) Q[i].ans=L;
        return;
    }
    rr int mid=(L+R)>>1,now=0,cntl=0,cntr=0;
    for (rr int i=l;i<=r;++i)
    if (Q[i].bel==2){
        if (query(dfn[Q[i].now]+son[Q[i].now]-1)-query(dfn[Q[i].now]-1)==now) Ql[++cntl]=Q[i];
            else Qr[++cntr]=Q[i];
    }else{
        if (zz[Q[i].now]<=mid) Ql[++cntl]=Q[i];
        else{
            rr int t=Q[i].bel?-1:1; Qr[++cntr]=Q[i];
            now+=t,Update(xx[Q[i].now],yy[Q[i].now],t,Lca[Q[i].now]);
        }
    }
    for (rr int i=1;i<=cntr;++i) if (Qr[i].bel!=2)
        Update(xx[Qr[i].now],yy[Qr[i].now],Qr[i].bel?1:-1,Lca[Qr[i].now]);
    for (rr int i=1;i<=cntl;++i) Q[l+i-1]=Ql[i];
    for (rr int i=1;i<=cntr;++i) Q[l+cntl+i-1]=Qr[i];
    if (cntl) update(L,mid,l,l+cntl-1);
    if (cntr) update(mid+1,R,l+cntl,r);
}
signed main(){
	n=iut(); q=iut();
	for (rr int i=1;i<n;++i){
		rr int x=iut(),y=iut();
		e[++k]=(node){y,ls[x]},ls[x]=k,
		e[++k]=(node){x,ls[y]},ls[y]=k;
	}
	dfs1(1,0),dfs2(1,1);
	for (rr int i=1;i<=q;++i){
		Q[i].bel=iut(),Q[i].rk=Q[i].now=i;
		if (!Q[i].bel) xx[i]=iut(),yy[i]=iut(),mx=max(mx,zz[i]=iut()),Lca[i]=lca(xx[i],yy[i]);
		    else Q[i].now=iut();
	}
    update(-1,mx,1,q),sort(Q+1,Q+1+q,cmp);
	for (rr int i=1;i<=q;++i) if (Q[i].bel==2){
    	if (~Q[i].ans) print(Q[i].ans);
    	    else putchar('-'),putchar(49);
    	putchar(10);
    }
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值