树上差分
树上差分
类似普通差分,一般询问所有链修改后单点/链查询。
原理
这里只说一下点权,边权注意一下减法的位置。
将 \(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日报
例题
-
板子
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); }
-
注意一下 \(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'); }
-
想到了二分答案基本就没问题。
二分 \(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); }
——————————————————————————————————————————————————————————————————————————————————————————————————————————
本文来自博客园,作者:xrlong,转载请注明原文链接:https://www.cnblogs.com/xrlong/articles/17591021.html