树上差分

树上差分

类似普通差分,一般询问所有链修改后单点/链查询。

原理

这里只说一下点权,边权注意一下减法的位置。

\(x \to y\) 的路径上每个点点权 \(+1\)

类比普通差分,考虑先标记在通过dfs回溯还原。

所以可以将 \(x+1,y+1,\operatorname{lca}(x,y)-1,fa_{\operatorname{lca}(x,y)}-1\)

边权就是 \(x+1,y+1,\operatorname{lca}(x,y)-2\)

详解可以看 luogu日报

例题

  1. MAX Flow_P

    板子

    CODE
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long llt;
    #define For_to(i,x) for(int i=hd[x];~i;i=nt[i])
    const int N=5e4+4;
    int hd[N],nt[N<<1],to[N<<1],tot_,fa[N][21],dep[N],n,k,dfc[N],ans[N];
    bool vis[N];
    
    namespace OI{
    	template<typename T>
    	inline void read(T &x){
    		char s=getchar();x=0;bool pd=false;
    		while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
    		while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
    		if(pd) x=-x;
    	}
    	template<typename T,typename... Args>
    	inline void read(T& x,Args&... args){read(x);read(args...);}
    	template<typename T>
    	inline void write(T x){
    		static T st[45];T top=0;
    		if(x<0)x=~x+1,putchar('-');
    		do{st[top++]=x%10;}while(x/=10);
    		while(top)putchar(st[--top]^48);
    	}
    	inline void write(const char c){putchar(c);}
    	inline void write(const char c[]){
    		int len=strlen(c);
    		for(int i=0;i<len;i++) putchar(c[i]);
    	}
    	template<typename T,typename... Args>
    	inline void write(T x,Args... args){write(x);write(args...);}
    }
    #define read OI::read
    #define write OI::write
    namespace TO{
    	inline void init(){memset(hd,-1,sizeof hd);}
    	inline void add(int x,int y){
    		to[tot_]=y,nt[tot_]=hd[x],hd[x]=tot_++;
    	}
    }
    namespace LCA{
    	void init(int x,int d){
    		dep[x]=d;
    		For_to(i,x){
    			int y=to[i];
    			if(!dep[y]){
    				fa[y][0]=x;
    				for(int i=1;i<=20;i++) fa[y][i]=fa[fa[y][i-1]][i-1];
    				init(y,d+1);
    			}
    		}
    	}
    	int get(int x,int y){
    		if(dep[x]<dep[y]) swap(x,y);
    		for(int i=20;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    		if(x==y) return x;
    		for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    }
    namespace ANS{
    	void dfs(int x){
    		vis[x]=1;
    		For_to(i,x){
    			int y=to[i];
    			if(!vis[y])
    				dfs(y),ans[x]+=ans[y];
    		}
    		ans[x]+=dfc[x];
    	}
    	void get(int x){
    		memset(vis,0,sizeof vis);
    		dfs(x);
    	}
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	TO::init();
    	read(n,k);
    	for(int i=1;i<n;i++){
    		int x,y;read(x,y);
    		TO::add(x,y),TO::add(y,x);
    	}
    	LCA::init(1,1);
    	for(int i=1;i<=k;i++){
    		int x,y,z;read(x,y);
    		z=LCA::get(x,y);
    		dfc[x]++,dfc[y]++,dfc[z]--,dfc[fa[z][0]]--;
    	}
    	ANS::get(1);int ma=-1;
    	for(int i=1;i<=n;i++) ma=max(ma,ans[i]);
    	write(ma);
    }
    
  2. 松鼠的新家

    注意一下 \(a_1 \to a_2 \to a_3\) 拆成 \(a_1 \to a_2\)\(a_2 \to a_3\) 会重复计算 \(a_2\),直接减掉

    COED
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long llt;
    #define For_to(i,x) for(int i=hd[x];~i;i=nt[i])
    const int N=3e5+4;
    int hd[N],nt[N<<1],to[N<<1],tot_,fa[N][21],dep[N],n,k,dif[N],ans[N],a[N];
    bool vis[N];
    
    namespace OI{
    	template<typename T>
    	inline void read(T &x){
    		char s=getchar();x=0;bool pd=false;
    		while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
    		while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
    		if(pd) x=-x;
    	}
    	template<typename T,typename... Args>
    	inline void read(T& x,Args&... args){read(x);read(args...);}
    	template<typename T>
    	inline void write(T x){
    		static T st[45];T top=0;
    		if(x<0)x=~x+1,putchar('-');
    		do{st[top++]=x%10;}while(x/=10);
    		while(top)putchar(st[--top]^48);
    	}
    	inline void write(const char c){putchar(c);}
    	inline void write(const char c[]){
    		int len=strlen(c);
    		for(int i=0;i<len;i++) putchar(c[i]);
    	}
    	template<typename T,typename... Args>
    	inline void write(T x,Args... args){write(x);write(args...);}
    }
    #define read OI::read
    #define write OI::write
    namespace TO{
    	inline void init(){memset(hd,-1,sizeof hd);}
    	inline void add(int x,int y){
    		to[tot_]=y,nt[tot_]=hd[x],hd[x]=tot_++;
    	}
    }
    namespace LCA{
    	void init(int x,int d){
    		dep[x]=d;
    		For_to(i,x){
    			int y=to[i];
    			if(!dep[y]){
    				fa[y][0]=x;
    				for(int i=1;i<=20;i++) fa[y][i]=fa[fa[y][i-1]][i-1];
    				init(y,d+1);
    			}
    		}
    	}
    	int get(int x,int y){
    		if(dep[x]<dep[y]) swap(x,y);
    		for(int i=20;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    		if(x==y) return x;
    		for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    }
    namespace T_DIF{
    	inline void init(){memset(dif,0,sizeof dif);}
    	inline void add(int x,int y){
    		int z=LCA::get(x,y);
    		dif[x]++,dif[y]++,dif[z]--,dif[fa[z][0]]--;
    	}
    	void re(int *aa,int x,int fa){
    		aa[x]=dif[x];
    		For_to(i,x){
    			int y=to[i];
    			if(y!=fa)
    				re(aa,y,x),aa[x]+=aa[y];
    		}
    	}
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	TO::init();
    	read(n);
    	for(int i=1;i<=n;i++) read(a[i]);
    	for(int i=1;i<n;i++){
    		int x,y;read(x,y);
    		TO::add(x,y),TO::add(y,x);
    	}
    	LCA::init(1,1);
    	for(int i=1;i<n;i++){
    		int x=a[i],y=a[i+1];
    		T_DIF::add(x,y);
    	}
    	T_DIF::re(ans,1,0);
    	for(int i=2;i<=n;i++) ans[a[i]]--;
    	for(int i=1;i<=n;i++) write(ans[i],'\n');
    }
    
  3. 运输计划

    想到了二分答案基本就没问题。

    二分 \(ans\)(就是最少时间

    考虑 check,就是判断是否存在一条将所有不合法的路径(总时间 \(> ans\))的共边,可以使时间最大的边合法。

    求共边可以树上查分,判断暴力即可。

    CODE
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long llt;
    #define For_to(i,x) for(int i=hd[x];~i;i=nt[i])
    const int N=3e5+4;
    int hd[N],nt[N<<1],to[N<<1],lw[N<<1],wt[N],tot_,fa[N][21],dep[N],n,m,dis[N],dif[N],cu[N],cv[N],ans[N],lca[N];
    
    namespace OI{
    	template<typename T>
    	inline void read(T &x){
    		char s=getchar();x=0;bool pd=false;
    		while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
    		while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
    		if(pd) x=-x;
    	}
    	template<typename T,typename... Args>
    	inline void read(T& x,Args&... args){read(x);read(args...);}
    	template<typename T>
    	inline void write(T x){
    		static T st[45];T top=0;
    		if(x<0)x=~x+1,putchar('-');
    		do{st[top++]=x%10;}while(x/=10);
    		while(top)putchar(st[--top]^48);
    	}
    	inline void write(const char c){putchar(c);}
    	inline void write(const char c[]){
    		int len=strlen(c);
    		for(int i=0;i<len;i++) putchar(c[i]);
    	}
    	template<typename T,typename... Args>
    	inline void write(T x,Args... args){write(x);write(args...);}
    }
    #define read OI::read
    #define write OI::write
    namespace LCA{
    	inline int get(int x,int y){
    		if(dep[x]<dep[y]) swap(x,y);
    		for(int i=20;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    		if(x==y) return x;
    		for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    		return fa[x][0];
    	}
    }
    namespace TO{
    	inline void into(){memset(hd,-1,sizeof hd);}
    	inline void add(int x,int y,int w){
    		to[tot_]=y,nt[tot_]=hd[x],hd[x]=tot_,lw[tot_++]=w;
    	}
    	void init(int x,int d){
    		dep[x]=d,dis[x]+=wt[x];
    		For_to(i,x){
    			int y=to[i];
    			if(!dep[y]){
    				wt[y]=lw[i];
    				fa[y][0]=x;
    				for(int i=1;i<=20;i++) fa[y][i]=fa[fa[y][i-1]][i-1];
    				dis[y]+=dis[x];
    				init(y,d+1);
    			}
    		}
    	}
    	inline int gdis(int i){
    		int x=cu[i],y=cv[i],z=lca[i];
    		return dis[x]+dis[y]-dis[z]*2; 
    	}
    }
    namespace T_DIF{
    	inline void init() {memset(dif, 0, sizeof dif);}
    	inline void add(int i){
    		int x=cu[i],y=cv[i],z=lca[i];
    		dif[x]++,dif[y]++,dif[z]-=2;
    	}
    	void re(int x,int fa){
    		ans[x]=dif[x];
    		For_to(i,x){
    			int y=to[i];
    			if(y!=fa)
    				re(y,x),ans[x]+=ans[y];
    		}
    	}
    }
    namespace DCK{
    	inline bool check(int x){
    		T_DIF::init();
    		int ma=0,cnt=0;
    		for(int i=1;i<=m;i++){
    			int y=TO::gdis(i);
    			if(y>x)
    				cnt++,T_DIF::add(i);
    		}
    		T_DIF::re(1,0);
    		for(int i=1;i<=n;i++)
    			if(ans[i]==cnt) ma=max(wt[i],ma);
    		for(int i=1;i<=m;i++){
    			int y=TO::gdis(i)-ma;
    			if(y>x) return false;
    		}
    		return true;
    	}
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	TO::into();
    	read(n,m);
    	for(int i=1;i<n;i++){
    		int x,y,w; read(x,y,w);
    		TO::add(x,y,w),TO::add(y,x,w);
    	}
    	TO::init(1,1);
    	for(int i=1;i<=m;i++) read(cu[i],cv[i]),lca[i]=LCA::get(cu[i],cv[i]);
    	int l=0,r=0x3f3f3f3f;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(DCK::check(mid)) r=mid-1;
    		else l=mid+1;
    	}
    	write(l);
    }   
    
posted @ 2023-07-30 09:41  xrlong  阅读(7)  评论(0编辑  收藏  举报