zoukankan      html  css  js  c++  java
  • 树上差分略解

    树上差分一般用于和树上路径有关的统计,对于一条路径((S,T)),我们一般修改他们的(d[S],d[T],d[LCA])的值来达到目的,而答案通常通过统计子树和来完成,复杂度显然为(O(n))

    例一:最大流

    这题是一道树上差分的板子题。

    显然用差分给对(d[S],d[T])加一,(d[LCA],d[las[LCA][0]])减一,然后求子树和就相当于给树上路径节点加一。

    不多(BB),直接放代码。

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    const int N=50010;
    int n,m,num_edge;
    int head[N<<1],d[N],las[N][21],Dep[N];
    struct Edge{int next,to;} edge[N<<1];
    inline void Add(int from,int to)
    {
    	edge[++num_edge].next=head[from];
    	edge[num_edge].to=to;
    	head[from]=num_edge;
    }
    inline void Dfs(int pos,int fa)
    {
    	las[pos][0]=fa;Dep[pos]=Dep[fa]+1;
    	for(int i=0;i<20;i++) las[pos][i+1]=las[las[pos][i]][i];
    	for(int i=head[pos];i;i=edge[i].next)
    		if(edge[i].to!=fa) Dfs(edge[i].to,pos);
    }
    inline int LCA(int u,int v)
    {
    	if(Dep[u]<Dep[v]) swap(u,v);
    	for(int i=20;i>=0;i--) if(Dep[v]<=Dep[u]-(1<<i)) u=las[u][i];
    	if(u==v) return u;
    	for(int i=20;i>=0;i--) if(las[u][i]!=las[v][i]) u=las[u][i],v=las[v][i];
    	return las[u][0];
    }
    inline void Dfs_For_Ans(int pos,int fa)
    {
    	for(int i=head[pos];i;i=edge[i].next)
    		if(edge[i].to!=fa)
    			Dfs_For_Ans(edge[i].to,pos),d[pos]+=d[edge[i].to];
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("A.in","r",stdin);
    #endif
    	n=read();m=read();
    	for(int i=1,u,v;i<n;i++)
    		u=read(),v=read(),Add(u,v),Add(v,u);
    	Dfs(1,0);
    	for(int i=1;i<=m;i++)
    	{
    		int s=read(),t=read(),lca=LCA(s,t);
    		//printf("%d:%d
    ",i,lca);
    		d[s]++,d[t]++,d[lca]--,d[las[lca][0]]--;
    	}
    	int ans=-1;Dfs_For_Ans(1,0);
    	for(int i=1;i<=n;i++) ans=max(ans,d[i]);
    	printf("%d",ans);
    }
    

    例二:NOIP2015运输计划

    这一题有点意思,相当于求修改后的最长路径最短为多少,显然二分,考虑如何(Check)

    二分出(s)后,找到所有大于(s)的路径的重边,

    然后判断一下即可。

    注意在找重边时用差分做,即在(LCA)处减(2)(S,T)处加(1)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int f=1,w=0;char x=0;
        while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();}
        while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();}
        return w*f;
    }
    const int N=300010;
    int n,m,num_edge,Tot;
    int d[N],Dep[N],Tep[N];
    int head[N<<1],las[N][30];
    struct Edge{int next,to,dis;} edge[N<<1];
    struct Group{int from,to,len,lca;} Pln[N];
    inline void Add(int from,int to,int dis)
    {
    	edge[++num_edge].next=head[from];
    	edge[num_edge].dis=dis;
    	edge[num_edge].to=to;
    	head[from]=num_edge;
    }
    inline void Dfs(int pos,int fa)
    {
    	las[pos][0]=fa,Dep[pos]=Dep[fa]+1;
    	for(int i=0;i<25;i++) las[pos][i+1]=las[las[pos][i]][i];
    	for(int i=head[pos];i;i=edge[i].next)
    		if(edge[i].to!=fa)
    			d[edge[i].to]=d[pos]+edge[i].dis,Dfs(edge[i].to,pos);
    }
    inline int LCA(int u,int v)
    {
    	if(Dep[u]<Dep[v]) swap(u,v);
    	for(int i=25;i>=0;i--) if(Dep[u]-(1<<i)>=Dep[v]) u=las[u][i];
    	if(u==v) return u;
    	for(int i=25;i>=0;i--) if(las[u][i]!=las[v][i]) u=las[u][i],v=las[v][i];
    	return las[u][0];
    }
    inline void Dfs_For_CF(int pos,int fa)
    {
    	for(int i=head[pos];i;i=edge[i].next)
    		if(edge[i].to!=fa)
    			Dfs_For_CF(edge[i].to,pos),Tep[pos]+=Tep[edge[i].to];
    }
    inline bool Check(int s)
    {
    	int Cnt=0,Max=0;
    	memset(Tep,0,sizeof(Tep));
    	for(int i=1;i<=m;i++)
    		if(Pln[i].len>s)
    		{
    			Tep[Pln[i].from]++,Tep[Pln[i].to]++;Cnt++;
    			Tep[Pln[i].lca]-=2;Max=max(Max,Pln[i].len-s);
    		}
    	if(!Cnt) return 1;Dfs_For_CF(1,0);
    	for(int i=2;i<=n;i++) if(Tep[i]==Cnt&&d[i]-d[las[i][0]]>=Max) return 1;
    	return 0;
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("A.in","r",stdin);
    #endif
    	n=read(),m=read();
    	for(int i=1,u,v,d;i<n;i++)
    		u=read(),v=read(),d=read(),Add(u,v,d),Add(v,u,d),Tot+=d;
    	d[1]=0;Dfs(1,0);
    	for(int i=1;i<=m;i++)
    	{
    		Pln[i].from=read(),Pln[i].to=read();
    		Pln[i].lca=LCA(Pln[i].from,Pln[i].to);
    		Pln[i].len=d[Pln[i].from]+d[Pln[i].to]-2*d[Pln[i].lca];
    	}
    	int l=0,r=Tot;
    	while(l<r)
    	{
    		int mid=(l+r)>>1;
    		if(Check(mid)) r=mid;
    		else l=mid+1;
    	}
    	printf("%d",l);
    }
    
    
  • 相关阅读:
    JSP中文乱码的一点研究
    Eclipse 导入外部项目无法识别为web项目并且无法在部署到tomcat下
    DOS命令学习
    Oracle学习笔记(一)
    登录时候的图片验证码的实现
    DIY随机数产生类
    使用VS2010建模
    Random快速连续产生不相同随机数
    魔兽争霸3冰封王座英雄以及兵种语言
    取两个地图坐标之间的距离
  • 原文地址:https://www.cnblogs.com/wo-shi-zhen-de-cai/p/11361012.html
Copyright © 2011-2022 走看看