差分

T1:Color the ball
本 题 是 差 分 的 模 板 题 本题是差分的模板题
差 分 一 下 两 两 之 间 的 差 值 , 区 间 加 影 响 的 差 值 就 是 c f [ l ] 和 差分一下两两之间的差值,区间加影响的差值就是cf[l]和 cf[l]
c f [ r + 1 ] , 再 做 一 次 前 缀 和 就 做 完 了 cf[r+1],再做一次前缀和就做完了 cf[r+1]

#include
using namespace std;

const int N=1e5+5;
int n;
int a,b,p[N];
int main(){
	scanf("%d",&n);
	while(n){
		memset(p,0,sizeof(p));
		for(int i=1;i<=n;i++){
			scanf("%d%d",&a,&b);
			p[a]++,p[b+1]--;
		}
		int sum=0;
		for(int i=1;i<=n;i++)
		{
			sum+=p[i];
			printf("%d ",sum);
		}
		puts("");
		scanf("%d",&n);
	}
}

T2:数列游戏
线 段 树 是 可 以 的 线段树是可以的 线
因 为 修 改 和 询 问 是 分 开 的 , 所 以 我 们 可 以 先 差 分 , 计 算 出 每 个 数 , 再 前 缀 和 , 然 后 就 可 以 求 区 间 和 了 因为修改和询问是分开的,所以我们可以先差分,计算出每个数,再前缀和,然后就可以求区间和了

#include
using namespace std;

typedef long long ll;
const ll mod=1e9+7;
const int N=1e6+5;
int n,a,b;
ll cf[N],s[N],aa[N];
int main(){
	scanf("%d%d%d",&n,&a,&b);
	for(int i=1;i<=a;i++){
		int l,r;
		ll c;
		scanf("%d%d%lld",&l,&r,&c);
		cf[l]=((c%mod+cf[l])%mod+mod)%mod;
		cf[r+1]=((cf[r+1]-(c%mod))%mod+mod)%mod;
	}
	for(int i=1;i<=n;i++){
		aa[i]=((aa[i-1]+cf[i])%mod+mod)%mod;
		s[i]=((s[i-1]+aa[i])%mod+mod)%mod;
	}
	for(int i=1;i<=b;i++){
		int l,r;
		scanf("%d%d",&l,&r);
		ll ans=((s[r]-s[l-1])%mod+mod)%mod;
		printf("%lld\n",ans);
	}
}

T3:松鼠的新家

代码中的cf[i]即表示题解中的ans[i]

对 于 路 径 上 的 值 相 邻 两 个 数 可 以 看 做 区 间 加 对于路径上的值相邻两个数可以看做区间加
本 题 很 像 树 上 差 分 , 所 以 我 们 直 接 树 上 差 分 , 我 们 将 节 点 s 的 答 案 看 做 子 树 的 和 本题很像树上差分,所以我们直接树上差分,我们将节点s的答案看做子树的和 s
修 改 就 是 修改就是
1. l c a 是 s 或 t , 直 接 a n s [ s ] 或 a n s [ t ] + + , a n s [ f a [ l c a ] ] − − 1.lca是s或t,直接ans[s]或ans[t]++,ans[fa[lca]]-- 1.lcastans[s]ans[t]++,ans[fa[lca]]
2. l c a 不 是 s 或 t , 将 a n s [ s ] + + , a n s [ t ] + + , a n s [ l c a ] − − , a n s [ f a [ l c a ] ] − − 2.lca不是s或t,将ans[s]++,ans[t]++,ans[lca]--,ans[fa[lca]]-- 2.lcastans[s]++,ans[t]++,ans[lca],ans[fa[lca]]
由 于 我 还 不 会 倍 增 求 l c a , 所 以 我 直 接 上 树 剖 了 由于我还不会倍增求lca,所以我直接上树剖了 lca
注 意 点 : \color{blue}注意点:
1. 因 为 是 路 径 , 并 不 能 将 相 邻 节 点 加 两 次 , 所 以 每 次 区 间 加 的 起 点 和 终 点 有 点 难 判 断 , 所 以 不 妨 直 接 加 1.因为是路径,并不能将相邻节点加两次,所以每次区间加的起点和终点有点难判断,所以不妨直接加 1.
s , t 的 , 并 且 保 证 不 加 s ( 如 题 意 , 第 一 个 点 不 用 放 ) , 最 后 一 个 节 点 要 减 去 ( 如 题 意 ) , 所 以 最 后 还 需 讨 论 一 下 , 因 为 情 况 比 较 多 s,t的,并且保证不加s(如题意,第一个点不用放),最后一个节点要减去(如题意),所以最后还需讨论一下,因为情况比较多 s,ts()
2. 注 意 题 意 , 最 后 一 个 点 是 不 需 要 加 入 答 案 的 2.注意题意,最后一个点是不需要加入答案的 2.

讨 论 的 情 况 , 讨论的情况,

  1. s,t在一条链上,且s为lca,则ans[s]–,(因为s不用算,所以实际上要算s的子节点,又因为是子节点的父亲–,所以直接ans[s]–),ans[t]++
  2. s,t在一条链上,且t为lca,则ans[fa[s]]++(此时s不用算,因从fa[s]开始计算),ans[fa[v]]–
  3. s,t不在一条链上,所以ans[fa[s]]++,ans[t]++,ans[lca]–,ans[fa[lca]]–
#include
using namespace std;

const int N=3e5+5;
int n,a[N];
struct edge{
	int link,v;
}q[N<<1];
int head[N],cnt=0;
void put(int u,int v){q[++cnt].v=v;q[cnt].link=head[u];head[u]=cnt;}
int dfn[N],size[N],son[N],fa[N],id[N],num[N],headline[N],depth[N],cf[N];
void dfs1(int s,int fath){
	int maxson=0;
	size[s]++;depth[s]=depth[fath]+1;
	for(int i=head[s];i;i=q[i].link){
	      int v=q[i].v;
	      if(v==fath) continue; 
		  fa[v]=s;
	      dfs1(v,s);
	      size[s]+=size[v];
		  if(size[v]>maxson){
		  	maxson=size[v];
		  	son[s]=v;
		  }	  
	}
}
int tnt=0,cntid=0;
void dfs2(int s,int fath){
	dfn[s]=++tnt;
	if(son[s]){
	 id[son[s]]=id[s];headline[son[s]]=headline[s];
	 dfs2(son[s],s);
	}
	for(int i=head[s];i;i=q[i].link){
		int v=q[i].v;
		if(v==fath||v==son[s]) continue;
		id[v]=++cntid;num[cntid]=v;headline[v]=v;
		dfs2(v,s);
	}
}
int ans[N];
int find(int u,int v){
	while(headline[u]!=headline[v]){
		if(depth[headline[u]]<depth[headline[v]]) swap(u,v);
		u=fa[headline[u]];
	}
	if(headline[u]==headline[v]){
		if(depth[u]<depth[v])
		swap(u,v);
	    return v;
	}
}
void solve(int now){
	if(now>n) return;
	while(now<=n){
		if(now==2){
			int u=a[now-1],v=a[now];
			cf[v]++;
			now++;
			continue;
		}
		int u=a[now-1],v=a[now],uu=u,vv=v;;
		if(headline[uu]==headline[vv]){
			if(depth[uu]<depth[vv]) swap(uu,vv);
			int lca=vv;
			if(lca==u) {
				cf[u]--,cf[v]++;
			}
			if(lca==v){
				cf[fa[u]]++,cf[fa[v]]--;
			}
			now++;
			continue;
		}
		int lca=find(u,v);
		if(lca==0) lca=a[1];
		cf[fa[u]]++,cf[v]++,cf[lca]--,cf[fa[lca]]--;
		now++;
	}
}
int sum=0;
void dfs(int s,int fath){
    ans[s]=cf[s];
	for(int i=head[s];i;i=q[i].link){
		int v=q[i].v;
		if(v==fath) continue;
		dfs(v,s);
		ans[s]+=ans[v];
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		put(u,v),put(v,u);
	}
	put(0,a[1]),put(a[1],0);
	fa[a[1]]=0;
	dfs1(a[1],0);	
	id[a[1]]=++cntid;num[cntid]=a[1];headline[a[1]]=a[1];
	dfs2(a[1],0);
	solve(2);
	dfs(a[1],0);
	ans[a[n]]--;
	for(int i=1;i<=n;i++){
		printf("%d\n",ans[i]);
	}
}

T4:天天爱跑步

感谢这位大佬的博客让我明白了这道题
洛谷题面
不 知 道 这 题 和 差 分 有 什 么 关 系 , 但 既 然 放 到 这 个 专 题 中 , 就 当 做 是 吧 不知道这题和差分有什么关系,但既然放到这个专题中,就当做是吧
我 们 发 现 , 没 法 统 计 s 到 t 路 径 上 的 点 是 否 , 所 以 我 们 考 虑 点 S , 对 于 S , 我 们 考 虑 能 给 S 的 贡 献 我们发现,没法统计s到t路径上的点是否,所以我们考虑点S,对于S,我们考虑能给S的贡献 stSSS
这 样 , 我 们 根 据 N O I P 历 年 套 路 。 显 然 想 到 拆 分 路 径 , s — — l c a 和 l c a — — t 这样,我们根据NOIP历年套路。显然想到拆分路径,s——lca和lca——t NOIPslcalcat
对 于 s — — l c a 的 路 径 , 我 们 发 现 能 贡 献 给 点 S 需 满 足 : d e e p [ s ] − d e e p [ S ] = w [ S ] 对于s——lca的路径,我们发现能贡献给点S需满足:deep[s]-deep[S]=w[S] slcaSdeep[s]deep[S]=w[S]
对 于 l c a — — t 的 路 径 , 我 们 发 现 能 贡 献 给 点 S 需 满 足 : d i s [ i ] − w [ S ] = d e e p [ v ] − d e e p [ S ] ( 其 中 , d i s [ i ] 为 s — — t 的 路 径 长 , 这 个 可 以 在 计 算 l c a 的 时 候 顺 便 记 下 来 ) 对于lca——t的路径,我们发现能贡献给点S需满足:dis[i]-w[S]=deep[v]-deep[S](其中,dis[i]为s——t的路径长,这个可以在计算lca的时候顺便记下来) lcatSdis[i]w[S]=deep[v]deep[S](dis[i]stlca便)
画 个 图 就 能 明 白 画个图就能明白
化 简 一 下 式 子 , 我 们 发 现 化简一下式子,我们发现
d e e p [ S ] + w [ S ] = d e e p [ S ] deep[S]+w[S]=deep[S] deep[S]+w[S]=deep[S]
w [ S ] − d e e p [ S ] = d i s [ i ] − d e e p [ v ] w[S]-deep[S]=dis[i]-deep[v] w[S]deep[S]=dis[i]deep[v]
很 快 想 到 用 树 状 数 组 , 但 本 题 应 该 用 桶 很快想到用树状数组,但本题应该用桶
然后就做完了
注 意 点 : \color{green}注意点:

  1. w[S]-deep[S]可能会爆,所以左右同时加n,w[S]-deep[S]+n=dis[i]-deep[v]+n
  2. lca可以看成从s——lca,也可以看成lca——t,所以如果lca满足条件,要-1
  3. 倍增求lca时,跳的步数应该到0
#include
using namespace std;

const int N=3e5+5;
int n,m,head[N],cnt=0;
struct edge{
	int link,v;
}q[N<<1];
int h1[N],h2[N*3],hcnt1=0,hcnt2=0;
void put(int x,int y){
	q[++cnt].v=y;
	q[cnt].link=head[x];
	head[x]=cnt;
}
struct eg1{
	int link,v;
}q1[N<<1];
struct eg2{
	int link,v;
}q2[N<<1];
void put1(int x,int y){
	q1[++hcnt1].v=y;
	q1[hcnt1].link=h1[x];
	h1[x]=hcnt1;
}
void put2(int x,int y){
	q2[++hcnt2].v=y;
	q2[hcnt2].link=h2[x];
	h2[x]=hcnt2;
}
int d1[N*3],vals[N],d2[N*3],lg[N],w[N],fa[N][20],deep[N],s[N],t[N],lca[N],dis[N];
void dfs(int ss,int fath){
	deep[ss]=deep[fath]+1;
	for(int i=1;i<=lg[deep[ss]-1];i++){
		fa[ss][i]=fa[fa[ss][i-1]][i-1];
	}
	for(int i=head[ss];i;i=q[i].link){
		int v=q[i].v;
		if(v==fath) continue;
		fa[v][0]=ss;
		dfs(v,ss);
	}
}
int Lca(int x,int y){
	if(deep[x]<deep[y]) swap(x,y);
	while(deep[x]>deep[y]){
		x=fa[x][lg[deep[x]-deep[y]]];
	}
	if(x==y) return x;
	for(int i=lg[deep[x]-1];i>=0;i--){//能跳的一定要跳完,所以下界是0,因为当两步到lca时,还需要判断0步的点是不是lca 
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i],y=fa[y][i];
		}
	}
	return fa[x][0];
}
int valsum[N];
void solve(int ss,int fath){
	int tmp1=d1[deep[ss]+w[ss]],tmp2=d2[w[ss]-deep[ss]+n];
	for(int i=head[ss];i;i=q[i].link)
	{
		int v=q[i].v;
		if(v==fath) continue;
		solve(v,ss);
	}
	d1[deep[ss]]+=vals[ss];
	for(int i=h2[ss];i;i=q2[i].link){
		int v=q2[i].v;
		d2[dis[v]-deep[ss]+n]++;
	}
	valsum[ss]+=d1[deep[ss]+w[ss]]-tmp1+d2[w[ss]-deep[ss]+n]-tmp2;
	for(int i=h1[ss];i;i=q1[i].link){
		int v=q1[i].v;
		int ls=s[i],rt=t[i];
		d1[deep[ls]]--;//只出一个点,其它的点不用出,所以不是dp[deep[ls]]-=vals[ls] 
		d2[dis[v]-deep[rt]+n]--;
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		put(u,v),put(v,u);
	}
	fa[1][0]=1;
	lg[1]=0;
	for(int i=2;i<=n;i++){
		lg[i]=lg[i>>1]+1;
	}
	dfs(1,0);
	for(int i=1;i<=n;i++){
		scanf("%d",&w[i]);
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&s[i],&t[i]);
	    lca[i]=Lca(s[i],t[i]);
	    vals[s[i]]++;
		dis[i]=deep[s[i]]+deep[t[i]]-2*deep[lca[i]];
	    put1(lca[i],i);
	    put2(t[i],i);
	    if(deep[lca[i]]+w[lca[i]]==deep[s[i]]) valsum[lca[i]]--;//如果lca有贡献,我们发现,lca既可以看成是s-lca的,也可以看成是lca-t的贡献,
		                                                        //会重复计算,所以判断一下,提前减去
																//直接看是否满足条件,看看有没有贡献 
	}
	solve(1,0);
	for(int i=1;i<=n;i++){
		printf("%d ",valsum[i]);
	}
}

你可能感兴趣的:(例题,差分,差分,例题,noip)