差分/树上差分

差分
概念也说不清楚,拿一个“奇妙”的类比:普通数组就像是原函数,普通数组的前缀和就像是积分。差分数组就像导数,差分数组的前缀和就是普通数组的对应数。在处理时,普通前缀和处理起来n,用的时候1;差分数组处理时是1,使用的时候是n。
题目Luogu P1083 借教室
可以使用线段树直接过,差分也可以解决这个题。
可以选择二分法,或者倒序减日程,这里选择倒序。

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int l[1000006];
int r[1000006];
long long c[1000006];
long long res[1000006];
int n,m;
long long diff[1000006];
long long sum;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%lld",&res[i]);
	for(int i=1;i<=m;i++){
		scanf("%lld%d%d",&c[i],&l[i],&r[i]);
		diff[l[i]]+=c[i];
		diff[r[i]+1]-=c[i];
	}
	int j=m;
	for(int i=1;i<=n;i++){
		sum+=diff[i];
		if(sum>res[i]){
			while(sum>res[i]){
				diff[l[j]]-=c[j];
				diff[r[j]+1]+=c[j];
				if(l[j]<=i&&r[j]>=i){
					sum-=c[j];
				}
				j--;
			}
		}
	}
	if(j==m)cout<<"0\n";
	else cout<<"-1"<<endl<<j+1<<endl;
	return 0;
}

这里需要按时间对所需房间数进行计算,是有这样连续的顺序的,因此可以使用差分。

树上差分
“奇妙” 无端的类比:
kmp-trie
差分-树上差分
对于树上某条路径上所有点或边进行同一加减操作,然后询问某一点(边)的权值。因为对树的dfs拥有特殊的顺序,所以可以类比普通的差分,可以把起点终点进行正向操作,对起点终点的lca和fa[lca]进行逆向操作,再进行dfs,子树的贡献和即为答案。
模板 Luogu P3128 [USACO15DEC]Max Flow P

#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
struct node{
		int from;
		int to;
		node(int a,int b):from(a),to(b){}
		node(){}
};
int ans;
struct LCA{
	vector<int>G[maxn];
	node Edge[2*maxn];
	int vis[500005];
	int dep[500005];
	int diff[500005]; 
	int t;
	int n;
	int father[maxn][23];
	void init(int u){
		t=0;
		n=u;
		for(int i=0;i<n;i++)G[i].clear();
		memset(vis,0,sizeof(vis));
	}
	void addEdge(int fy,int ty){
		Edge[t].from=fy;
		Edge[t].to=ty;
		G[fy].push_back(t);
		t++;
	}
	void dfs(int u,int d){
		vis[u]=1;
		dep[u]=d;
		for(int j=1;(1<<j)<=d;j++){
			father[u][j]=father[father[u][j-1]][j-1];
		}
		for(int i=0;i<G[u].size();i++){
			node & e=Edge[G[u][i]];
			if(!vis[e.to]){
				father[e.to][0]=u;
				dfs(e.to,d+1);
			}
		}
	}
	void reinit(){
		memset(vis,0,sizeof(vis));
	}
	void dfs_2(int u){
		vis[u]=1;
		for(int i=0;i<G[u].size();i++){
			node & e=Edge[G[u][i]];
			if(!vis[e.to]){
				dfs_2(e.to);
				diff[u]+=diff[e.to];
			}
		}
		ans=max(ans,diff[u]);
	}
	int lca(int x,int y){
		int a=x;
		int b=y;
		if(dep[a]<dep[b])swap(a,b); 
		for(int i=22;i>=0;i--){
			if(dep[father[a][i]]>=dep[b])a=father[a][i];
			if(a==b)return a;
		}
		for(int i=22;i>=0;i--){
			if(father[a][i]!=father[b][i]){
				a=father[a][i];
				b=father[b][i];
			}
		}
		return father[a][0];
	}
}LcA;
int n,m,s;
int main(){
	scanf("%d%d",&n,&m); 
	for(int i=1;i<n;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		LcA.addEdge(a,b);
		LcA.addEdge(b,a);
	}
	LcA.dfs(1,1);
	/*for(int i=1;i<=n;i++){
		printf("%d: ",i);
		for(int j=0;(1<<j)<=LcA.dep[i];j++){
			printf("%d ",LcA.father[i][j]);
		}
		printf("\n");
	}*/
	for(int i=1;i<=m;i++){
		int x;int y;
		scanf("%d%d",&x,&y);
		LcA.diff[x]++;
		LcA.diff[y]++;
		int anc=LcA.lca(x,y);
		LcA.diff[anc]--;
		LcA.diff[LcA.father[anc][0]]--;
	}
	LcA.reinit();
	LcA.dfs_2(1);
	cout<<ans<<endl;
	return 0;
}

蒟蒻只会倍增LCA 只是模板数据没有卡。orz

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值