树上差分

https://www.cnblogs.com/cjoierljl/p/8728215.html

看的是这个的总结

https://www.luogu.com.cn/problem/P3128

模板题

P3128 [USACO15DEC]Max Flow P

FJ给他的牛棚的N(2≤N≤50,000)个隔间之间安装了N-1根管道,隔间编号从1到N。所有隔间都被管道连通了。

FJ有K(1≤K≤100,000)条运输牛奶的路线,第i条路线从隔间si运输到隔间ti。一条运输路线会给它的两个端点处的隔间以及中间途径的所有隔间带来一个单位的运输压力,你需要计算压力最大的隔间的压力是多少。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=5e4+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int n,q,tot;
int head[maxn],to[maxn*2],nex[maxn*2];
int dif[maxn],sta[maxn];
int vis[maxn],dep[maxn],fa[maxn][21];
void adde(int u,int v){
	to[++tot]=v;
	nex[tot]=head[u];
	head[u]=tot;
	to[++tot]=u;
	nex[tot]=head[v];
	head[v]=tot;
}
void dfs(int u,int father){
	dep[u]=dep[father]+1;
	for(int i=0;i<=19;i++) fa[u][i+1]=fa[fa[u][i]][i];
	for(int i=head[u];i;i=nex[i]){
		int v=to[i];
		if(v==father) continue;
		fa[v][0]=u;
		dfs(v,u);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=19;i>=0;i--){
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];  //这里是大于等于,看吧自己写就是这些问题
		 
		if(x==y) return x;
	}
	for(int i=19;i>=0;i--){
		if(fa[x][i]!=fa[y][i]) {
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
int ans=-INF; 
void dfs_dif(int x){
	for(int i=head[x];i;i=nex[i]){
		int v=to[i];
		if(v==fa[x][0]) continue;
		dfs_dif(v);
		dif[x]+=dif[v];
	}
 	ans=max(ans,dif[x]);
//	cout<<dif[x]<<"  ";
}

int main(){
	scanf("%d %d",&n,&q);
	int x,y;
	for(int i=1;i<n;i++){
		scanf("%d %d",&x,&y);
		adde(x,y);
	}
	dfs(1,0);
	for(int i=0;i<q;i++){
		scanf("%d %d",&x,&y);
		//adde(x,y);
		dif[x]++;
		dif[y]++;
		int lcaa=lca(x,y);
		dif[fa[lcaa][0]]--;
		dif[lcaa]--;
	}
	adde(0,1); //建立了一个虚拟源点//0上面也会有标记,所以上面的东西也要弄掉 
	dfs_dif(0);
	printf("%d
",ans) ;
return 0;
}

  另一道也是模板题

https://www.luogu.com.cn/problem/P3258

P3258 [JLOI2014]松鼠的新家

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有nn 个房间,并且有 n-1n1 根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。

松鼠想邀请小熊前来参观,并且还指定一份参观指南,他希望小熊能够按照他的指南顺序,先去 a_1a1,再去 a_2a2,……,最后到 a_nan,去参观新家。可是这样会导致重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

小熊是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间 a_nan 是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

输入格式

第一行一个正整数 nn,表示房间个数第二行 nn 个正整数,依次描述 a_1, a_2,cdots,a_na1,a2,,an

接下来 n-1n1 行,每行两个正整数 x,yx,y,表示标号 xx 和 yy 的两个房间之间有树枝相连。

输出格式

一共 nn 行,第 ii 行输出标号为 ii 的房间至少需要放多少个糖果,才能让小熊有糖果吃。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=3e5+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
int n,q,tot;
int head[maxn],to[maxn*2],nex[maxn*2];
int dif[maxn];
int dep[maxn],fa[maxn][21];
int id[maxn];
void adde(int u,int v){
	to[++tot]=v;
	nex[tot]=head[u];
	head[u]=tot;
	to[++tot]=u;
	nex[tot]=head[v];
	head[v]=tot;
}
void dfs(int u,int father){
	dep[u]=dep[father]+1;
	for(int i=0;i<=19;i++) fa[u][i+1]=fa[fa[u][i]][i];
	for(int i=head[u];i;i=nex[i]){
		int v=to[i];
		if(v==father) continue;
		fa[v][0]=u;
		dfs(v,u);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=19;i>=0;i--){
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];  //这里是大于等于,看吧自己写就是这些问题
		 
		if(x==y) return x;
	}
	for(int i=19;i>=0;i--){
		if(fa[x][i]!=fa[y][i]) {
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
int ans=-INF; 
void dfs_dif(int x){
	for(int i=head[x];i;i=nex[i]){
		int v=to[i];
		if(v==fa[x][0]) continue;
		dfs_dif(v);
		dif[x]+=dif[v];
	}
 //	ans=max(ans,dif[x]);
//	cout<<dif[x]<<"  ";
}

int main(){
	scanf("%d",&n);
	int x,y;
	for(int i=1;i<=n;i++){
		scanf("%d",&id[i]);
	}
	for(int i=1;i<n;i++){
		scanf("%d %d",&x,&y);
		adde(x,y);
	}
	dfs(1,0);
	for(int i=2;i<=n;i++){
		int x=id[i-1],y=id[i];
		int lcaa=lca(x,y);
		dif[x]++;dif[y]++;
		dif[lcaa]--;
		dif[fa[lcaa][0]]--;
	}
	adde(0,1); //建立了一个虚拟源点//0上面也会有标记,所以上面的东西也要弄掉 
	dfs_dif(0);
	for(int i=2;i<=n;i++) dif[id[i]]--;
	for(int i=1;i<=n;i++) printf("%d
",dif[i]);
return 0;
}

  这道题是一本通上面的LCA第二道例题

1553:【例 2】暗的连锁

求方案的,注意结果每个值代表的结果

如果经过次数为0,本来删掉就会不连通,所以随便删掉一条附加边

如果经过次数为1,就必须删掉那条指定的边

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1e5+10;
const int maxm=2e5+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//树上差分的思想
//还不是很懂 ,这两篇题解很好
//https://www.cnblogs.com/ZhengkunJia/p/12275722.html
//https://blog.csdn.net/a_pathfinder/article/details/101034619
int n,m;
int head[maxm*2],to[maxm*2],nex[maxm*2],de[maxn],tot;
int lg[maxn],f[maxn][22];    //倍增数组
int dif[maxn],sta[maxn];  //差分、累计差分
int vis[maxn];
void add(int x,int y){
    to[++tot]=y;
    nex[tot]=head[x];
    head[x]=tot;
}
void lca_dfs(int u,int fa){
    de[u]=de[fa]+1;
    f[u][0]=fa;
    for(int i=1;(1<<i)<=de[u];i++){
        f[u][i]=f[f[u][i-1]][i-1];
    }
    for(int i=head[u];i;i=nex[i]){
        int v=to[i];
        if(v!=fa) lca_dfs(v,u);
    }
}
int lca(int x,int y){
    if(de[x]<de[y]) swap(x,y);
    while(de[x]>de[y]){
        x=f[x][lg[de[x]-de[y]]-1];
    }
    if(x==y) return x;
    for(int i=lg[de[x]]-1;i>=0;i--){
        if(f[x][i]!=f[y][i]){  //别忘了这个条件
                x=f[x][i];
                y=f[y][i];
        }
    }
    return f[x][0];
}
int dfs(int f){   //求出累计差分数组
    sta[f]=dif[f];
    vis[f]=1;
    for(int i=head[f];i;i=nex[i]){
        int v=to[i];
        if(vis[v]) continue;
        sta[f]+=dfs(v);
    }
    return sta[f];
}
int main(){
    scanf("%d %d",&n,&m);
    int x,y;
    for(int i=0;i<n-1;i++){
        scanf("%d %d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for(int i=1;i<=n;i++){
        lg[i]=lg[i-1]+(1<<lg[i-1]==i);  //这种写法
        //lg[i]=lg[i>>1]+1;
    }
    lca_dfs(1,0);
    for(int i=0;i<m;i++){
        scanf("%d %d",&x,&y);
        dif[x]++;
        dif[y]++;
        dif[lca(x,y)]-=2;
    }
    dfs(1);
    int ans=0;
    for(int i=1;i<=n;i++){
        if(sta[i]==0&&i!=1) ans+=m;  //why i!=1
        if(sta[i]==1) ans++;
    }
    printf("%d
",ans);
return 0;
}

https://www.luogu.com.cn/problem/P2680  

这道题好难TAT

P2680 运输计划

题目描述

公元20442044 年,人类进入了宇宙纪元。

L 国有 nn 个星球,还有 n-1n1 条双向航道,每条航道建立在两个星球之间,这 n-1n1 条航道连通了 L 国的所有星球。

小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 u_iui号星球沿最快的宇航路径飞行到 v_ivi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 jj,任意飞船驶过它所花费的时间为 t_jtj,并且任意两艘飞船之间不会产生任何干扰。

为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 mm 个运输计划。在虫洞建设完成后,这 mm 个运输计划会同时开始,所有飞船一起出发。当这 mm 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。

如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?

输入格式

第一行包括两个正整数 n, mn,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 11到 nn 编号。

接下来 n-1n1 行描述航道的建设情况,其中第 ii 行包含三个整数 a_i, b_iai,bi 和 t_iti,表示第 ii 条双向航道修建在 a_iai 与 b_ibi 两个星球之间,任意飞船驶过它所花费的时间为 t_iti

数据保证

接下来 mm 行描述运输计划的情况,其中第 jj 行包含两个正整数 u_juj 和 v_jvj,表示第 jj 个运输计划是从 u_juj 号星球飞往 v_jvj号星球。

输出格式

一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

有线段树、树状数组、边转点,求链长度,树上差分等等写法。。。

后面再看看

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=3e5+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//在一颗有边权的树上有m条路径,清零一条边的边权使得m条路径的最大值最小。
//这道题很难。。。。
//两种做法,第一种是树上差分+线段树+边转点
/*
涉及到的算法
树链剖分-边转点,求LCA操作,链剖分
线段树-建树以及查询和操作
树上差分
二分操作---这个二分的判断真的想不到 
*/ 
int read(){ //一个快读 
	char op=getchar();int x=0;
	while(op>'9'||op<'0') op=getchar();
	while(op<='9'&&op>='0') {
		x=(x<<3)+(x<<1)+'op'-'0';
		op=getchar();
	}
	return x;
}
int to[maxn*2],wei[maxn*2],nex[maxn*2],head[maxn],cnt;
int f[maxn]; //这个是记录经过这条边次数的,用于树上差分
int sum[maxn*2];
int a[maxn];
int dep[maxn],size[maxn],fa[maxn],son[maxn],top[maxn],id[maxn];  //用于树剖的 
int ch[maxn]; //代表当前点所带权
int num,maxlen,li,ro,tot,rans,ret,rnum,n,m;
//dfs序 
struct dann{
	int qx,qy,lca,v; //后面的代表这条链的长度
	bool operator < (const dann &a)const{
	return v<a.v;} 
}ans[maxn]; 
void adde(int x,int y,int w){
	to[++cnt]=y;nex[cnt]=head[x];wei[cnt]=w;head[x]=cnt;
	to[++cnt]=x;nex[cnt]=head[y];wei[cnt]=w;head[y]=cnt;
}

void build(int l,int r,int rt){  //线段树--建树 
	if(l==r) {
		sum[rt]=a[l];return;
	}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query(int l,int r,int rt){
	if(l>ro||r<li) return 0; //与这个区间没有重叠
	if(li<=l&&r<=ro) return sum[rt]; //在这个区间里面
	int mid=(l+r)>>1;
	return query(l,mid,rt<<1)+query(mid+1,r,rt<<1|1); 
}
//树剖的dfs1 
void dfs(int u){
	size[u]=1;
	for(int i=head[u];i;i=nex[i]){
		int v=to[i];
		if(v==fa[u]) continue;
		ch[v]=wei[i]; //深度大的那个点代表这条边
		dep[v]=dep[u]+1;
		fa[v]=u;
		dfs(v);
		size[u]+=size[v];
		if(size[v]>size[son[u]]) son[u]=v; 
	}
}
void dfs2(int u,int t){
	top[u]=t;
	id[u]=++num;
	a[num]=ch[u]; //这个是在线段树里面的 
	if(son[u]) dfs2(son[u],t);
	else return;
		for(int i=head[u];i;i=nex[i]){
			int v=to[i];
			if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
		}
}
void lca(int x,int y){//从x--y这样解决 
	++tot;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		li=id[top[x]];
		ro=id[x];
		ans[tot].v+=query(1,n,1); //这条链 
		x=fa[top[x]];
	}
	if(dep[x]>dep[y]) swap(x,y);
	ans[tot].lca=x;
	li=id[son[x]];
	ro=id[y];
	ans[tot].v+=query(1,n,1);
}
void dfs3(int u,int ff){
	for(int i=head[u];i;i=nex[i]){
		if(to[i]!=ff){
			dfs3(to[i],u);
			f[u]+=f[to[i]];
		}
	}
	if(f[u]==rnum&&a[id[u]]>ret) ret=a[id[u]];  //包含在所有链长度大于tt的中间所有链都经过的
	//并且长度最大 
}
bool check(int tt){
	int i;
	rnum=0;ret=0;
	memset(f,0,sizeof(f));
	for(i=m;i>=1;i--){
		if(ans[i].v<=tt) break;
		else{
			++f[ans[i].qx];
			++f[ans[i].qy];
			f[ans[i].lca]-=2;
			++rnum;
		}
	}
	if(i==m) return true;
	dfs3(1,0);
	return maxlen-ret<=tt; //如果最大值减去这条边都可以的话,那么就可以 
}
int solve(){
	sort(ans+1,ans+1+tot);
	int l=0,r=ans[tot].v;
	maxlen=ans[tot].v;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)) rans=mid,r=mid-1;
		else l=mid+1;
	}
	return rans;
}
int main(){
	n=read();
	m=read();
	int x,y,z;
	for(int i=1;i<n;i++){
		x=read();y=read();z=read();
		adde(x,y,z);
	}
	dep[1]=1;
	dfs(1);
	dfs2(1,1);
	build(1,n,1);
	for(int i=1;i<=m;i++){
		ans[i].qx=read();
		ans[i].qy=read();
		lca(ans[i].qx,ans[i].qy); 
	}
	printf("%d
",solve());
return 0;
}

//找不到哪里错了
#include<bits/stdc++.h>
#define N 300005
#define M 600005
using namespace std;
int n,m;
int nex[M],to[M],val[M],head[N],cnt;
int f[N],sum[N<<2],a[N],li,ro,tot,rans,rnum,ret,mlen;
int d[N],siz[N],fa[N],son[N],top[N],id[N],ch[N],num,maxlen;
struct daan
{
	int v,qx,qy,lca;
	bool operator < (const daan &A) const { return v<A.v; }
}ans[N];
inline int Mx(int x,int y){return x>y?x:y;}
inline int Mi(int x,int y){return x<y?x:y;}
inline void swp(int &x,int &y){int k=x;x=y;y=k;}
inline void add(int u,int v,int w)
{
	to[++cnt]=v;val[cnt]=w;nex[cnt]=head[u];head[u]=cnt;
	to[++cnt]=u;val[cnt]=w;nex[cnt]=head[v];head[v]=cnt;
}
inline int read()
{
	int x=0;char ch=getchar();
	while(ch>'9'||ch<'0')ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
void build(int l,int r,int rt)
{
	if(l==r){sum[rt]=a[l];return;}
	int mid=(l+r)>>1;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query(int l,int r,int rt)
{
	if(r<li||l>ro) return 0;
	if(li<=l&&r<=ro) return sum[rt];
	int mid=(l+r)>>1;
	return query(l,mid,rt<<1)+query(mid+1,r,rt<<1|1);
}
void dfs(int u)
{
	siz[u]=1;
	for(int i=head[u];i;i=nex[i])
	{
		if(to[i]!=fa[u])
		{
			ch[to[i]]=val[i],d[to[i]]=d[u]+1;fa[to[i]]=u;dfs(to[i]);siz[u]+=siz[to[i]];
			if(siz[to[i]]>siz[son[u]]) son[u]=to[i];
		}
	}
}
void dfs2(int u,int t)
{
	top[u]=t;id[u]=++num,a[num]=ch[u];
	if(son[u]) dfs2(son[u],t);
	else return;
	for(int i=head[u];i;i=nex[i]) if(to[i]!=son[u]&&to[i]!=fa[u]) dfs2(to[i],to[i]);
}
inline void LCA(int x,int y)
{
	++tot;
	while(top[x]!=top[y])
	{
		if(d[top[x]]<d[top[y]]) swp(x,y);
		li=id[top[x]],ro=id[x];ans[tot].v+=query(1,n,1);
		x=fa[top[x]];
	}
	if(d[x]>d[y]) swp(x,y);ans[tot].lca=x;
	li=id[son[x]],ro=id[y];ans[tot].v+=query(1,n,1);
}
void dfs3(int u,int ff)
{
	for(int i=head[u];i;i=nex[i])
	{
		if(to[i]!=ff)
		{
			dfs3(to[i],u);
			f[u]+=f[to[i]];
		}
	}
	if(f[u]==rnum&&a[id[u]]>ret) ret=a[id[u]];
}
inline bool check(int tt)
{
	int i;rnum=0,ret=0;
	memset(f,0,sizeof(f));
	for(i=m;i>=1;--i)
	{
		if(ans[i].v<=tt) break;
		else ++f[ans[i].qx],++f[ans[i].qy],--f[ans[i].lca],--f[ans[i].lca],++rnum;
	}if(i==m) return true;
	dfs3(1,0);
	return maxlen-ret<=tt;
}
int solve()
{
	sort(ans+1,ans+tot+1);
	int l=0,r=ans[tot].v;maxlen=ans[tot].v;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid)) rans=mid,r=mid-1;
		else l=mid+1;
	}
	return rans;
}
int main()
{
	n=read(),m=read();int x,y,z;
	for(int i=1;i<n;++i) x=read(),y=read(),z=read(),add(x,y,z),mlen=Mi(mlen,z);
	d[1]=1;dfs(1);dfs2(1,1);build(1,n,1);
	for(int i=1;i<=m;++i) ans[i].qx=read(),ans[i].qy=read(),LCA(ans[i].qx,ans[i].qy);
	printf("%d
",solve());
	return 0;
} 

  

原文地址:https://www.cnblogs.com/shirlybaby/p/13702782.html