【NOIP2016】【桶/线段树合并】【树上差分】天天爱跑步

【题目描述】
在这里插入图片描述

【思路】

这是道好题呀。考虑把一条路径(u,v)拆成两条:从u到lca(u,v),从lca(u,v)到v。下面我们以向上的路径为例讨论做法。对于一条向上的路径,它对一个点x有贡献当且仅当它覆盖了点x且 d e p [ u ] d e p [ x ] = w [ x ] dep[u]-dep[x]=w[x] 。即对于一个点x,我们需要统计有多少覆盖了它的路径的起点u满足 w [ x ] + d e p [ x ] = d e p [ u ] w[x]+dep[x]=dep[u] 。显然后面这个限制只需要维护一个数据结构就行了。那么问题在于如何使它只统计了覆盖自己的路径。注意到覆盖父亲和儿子的边只有一部分不同,我们可以考虑父亲继承子树信息,然后删除其中没有覆盖自己的路径。对于路径的删除和加入,我们可以用树上差分实现,在u处加入该边,在fa[lca(u,v)]处删除该边。每条路径最多被加入一次,删除一次,所以时间复杂度正确。可以使用线段树合并实现父亲继承子树信息。但是考虑到这是计数问题,并非最优化问题,答案满足可减性。所以我们不需要线段树合并保证只考虑了子树内的路径。我们可以在处理子树之前查询一次答案,在处理子树之后查询一次答案,两次答案的变化量就是子树中的路径对自己的贡献。这样使用一个桶就可以实现上述操作。我们按dfs的顺序依次处理每个点:
1.查询一次答案 a n s 1 ans_1
2.递归处理子树。
3.处理自己。
4.查询一次答案 a n s 2 ans_2 ,这个点的答案即 a n s 2 a n s 1 ans_2-ans_1

对于向下的路径依然可以类似操作,只需要查询满足 d e p [ u ] + d e p [ x ] 2 d e p [ l c a ( u , v ) ] = w [ x ] dep[u]+dep[x]-2*dep[lca(u,v)]=w[x] 的路径数量,移项即可维护。同样,我们在v处向桶中加入这条路径,在fa[lca(u,v)]删除这条路径即可。注意,为防止lca处重复统计了两条路径的贡献,我们可以选择一条路径在lca处删除,另一条在fa[lca]处删除。

代码:

#include<bits/stdc++.h>
#define re register
#define F(i,a,b) for(int re i=a;i<=b;++i)
#define D(i,a,b) for(int re i=a;i>=b;--i)
using namespace std;
const int N=6e5+5,M=3e5+5;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int red(){
    char re ch=nc();int re sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=((sum<<2)+sum<<1)+(ch^48),ch=nc();
    return sum;
}
int n,m,a,b;
int to[N],f[M],nxp[N],cnt=0;
inline void add(int u,int v){
	to[++cnt]=v;nxp[cnt]=f[u];f[u]=cnt;
	to[++cnt]=u;nxp[cnt]=f[v];f[v]=cnt; 
}
struct que{int pos,v;que(int x=0,int y=0){pos=x,v=y;}};
vector<que>g[2][M];
int fa[M],dep[M],top[M],son[M],ans[M],w[M],siz[M],num[2][N];
void dfs1(int u){int v,&x=son[u];
	siz[u]=1;dep[u]=dep[fa[u]]+1;
	for(int i=f[u];i;i=nxp[i]){
		if((v=to[i])==fa[u])continue;
		fa[v]=u;dfs1(v);siz[u]+=siz[v];
		if(siz[x]<siz[v])x=v;
	}
}
void dfs2(int u){
	if(son[u])top[son[u]]=top[u],dfs2(son[u]);
	for(int i=f[u];i;i=nxp[i])
		if(!top[to[i]])dfs2(top[to[i]]=to[i]);
}
inline int lca(int a,int b){
	while(top[a]^top[b])dep[top[a]]<dep[top[b]]?b=fa[top[b]]:a=fa[top[a]];
	return dep[a]<dep[b]?a:b;
}
inline int calc(int u){return num[0][w[u]+dep[u]]+num[1][w[u]-dep[u]+M];}
inline void insert(int u,int op){D(i,g[op][u].size()-1,0)num[op][g[op][u][i].pos]+=g[op][u][i].v;}
void dfs3(int u){
	int ans1=calc(u);
	for(int re i=f[u];i;i=nxp[i])if(dep[to[i]]>dep[u])dfs3(to[i]);
	insert(u,0);insert(u,1);
	ans[u]=calc(u)-ans1;
}
inline void print(int x){
	if(x>9)print(x/10);
	putchar(x%10^48);
}
int main()
{
	n=red();m=red();int rt=1;
	F(i,2,n)add(red(),red());
	F(i,1,n)w[i]=red();dfs1(rt),dfs2(top[rt]=rt);
	F(i,1,m){
		int u=red(),v=red(),x=lca(u,v),y=fa[x];
		g[0][u].push_back(que(dep[u],1));
		g[1][v].push_back(que(dep[u]-2*dep[x]+M,1));
		g[0][y].push_back(que(dep[u],-1));
		g[1][x].push_back(que(dep[u]-2*dep[x]+M,-1));
	}dfs3(rt);
	F(i,1,n)print(ans[i]),putchar(' ');
}
发布了106 篇原创文章 · 获赞 22 · 访问量 5477

猜你喜欢

转载自blog.csdn.net/weixin_44111457/article/details/102877075
今日推荐