Luogu P1600 [NOIP2016 提高组] 天天爱跑步

Solution

我们可以将玩家 \(i\) 跑步的路线分成两段:\(s_i\rightarrow\mathrm{LCA}(s_i,t_i)\)\(\mathrm{LCA}(s_i,t_i)\rightarrow t_i\)

\(\mathrm{dep}_i\) 表示树上 \(i\) 号节点的深度,\(\mathrm{fa}_i\) 表示树上 \(i\) 号节点的父节点。

若节点 \(x\) 可以观察到玩家 \(i\) 的行动,当且仅当:

  • 节点 \(x\)\(s_i\rightarrow\mathrm{LCA}(s_i,t_i)\) 的路径上,且有 \(\mathrm{dep}_{s_i}-\mathrm{dep}_x=w_x\)

  • 节点 \(x\)\(\mathrm{LCA}(s_i,t_i)\rightarrow t_i\) 的路径上,且有 \(\mathrm{dep}_{s_i}+\mathrm{dep}_x-2 \times \mathrm{dep}_{\mathrm{LCA}(s_i,t_i)}=w_x\)

两者互不干涉,于是分成两类讨论,答案相加即可。

如果节点 \(x\)\(s_i\rightarrow\mathrm{LCA}(s_i,t_i)\) 的路径上,那么移项得 \(\mathrm{dep}_{s_i}=\mathrm{dep}_x+w_x\)。这样做相当于在 \(s_i\rightarrow\mathrm{LCA}(s_i,t_i)\) 的路径上新增一个物品 \(\mathrm{dep}_{s_i}\)。询问每一个点 \(x\) 上的物品 \(\mathrm{dep}_x+w_x\) 的数量。通过树上差分,我们可以将其视作:该物品在 \(s_i\) 处出现,并在 \(\mathrm{fa}_{\mathrm{LCA}(s_i,t_i)}\) 处消失。我们称其为第一种情况。

如果节点 \(x\)\(\mathrm{LCA}(s_i,t_i)\rightarrow t_i\) 的路径上,那么移项得 \(\mathrm{dep}_{s_i}-2 \times \mathrm{dep}_{\mathrm{LCA}(s_i,t_i)}=w_x-\mathrm{dep}_x\)。这样做相当于在 \(\mathrm{LCA}(s_i,t_i)\rightarrow t_i\) 的路径上新增一个物品 \(\mathrm{dep}_{s_i}-2 \times \mathrm{dep}_{\mathrm{LCA}(s_i,t_i)}\)。询问每一个点 \(x\) 上的物品 \(w_x-\mathrm{dep}_x\) 的数量。通过树上差分,我们可以将其视作:该物品在 \(t_i\) 处出现,并在 \(\mathrm{LCA}(s_i,t_i)\) 处消失。我们称其为第二种情况。

对于每一次询问,我们存下每一个物品出现和消失的位置。与此同时,记录一个桶 \(\mathrm{cnt}_{0/1,i}\) 表示第一种情况和第二种情况下的类型 \(i\) 物品的数量。

考虑 DFS 遍历整棵树。递归到一个节点 \(x\),先存下目前的 \(\mathrm{cnt}_{0,\mathrm{dep}_x+w_x}\)\(\mathrm{cnt}_{1,w_x-\mathrm{dep}_x}\),然后查看 \(x\) 位置出现或消失的物品并更新 \(\mathrm{cnt}_{0/1}\),继续递归节点 \(x\) 的子节点。

递归完后,现在的 \(\mathrm{cnt}_{0,\mathrm{dep}_x+w_x}\) 和之前的 \(\mathrm{cnt}_{0,\mathrm{dep}_x+w_x}\) 的差值, 现在的 \(\mathrm{cnt}_{1,w_x-\mathrm{dep}_x}\) 和之前的 \(\mathrm{cnt}_{1,w_x-\mathrm{dep}_x}\) 的差值就是两种情况分别的答案。

Code

/* ChongYun */
#include<bits/stdc++.h>
#define int long long 
#define fir first
#define sec second
using namespace std;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*f;
}
int mov(int x){ return x+300001; }
int n,m,w[300005];
int s[300005],t[300005];
struct Edge{
	int to,nxt,val;
}e[300005<<1];
int hd[300005],ecnt=0;
void Link(int x,int y){
	++ecnt;
	e[ecnt].to=y;
	e[ecnt].nxt=hd[x];
	hd[x]=ecnt;
	return ;
} 
int fath[300005];
int dep[300005],dp[300005][21];
void initdfs(int x,int fa){
    fath[x]=fa; dep[x]=dep[fa]+1; dp[x][0]=fa;
	for(int i=1;(1<<i)<=dep[x];i++) dp[x][i]=dp[dp[x][i-1]][i-1];
	for(int i=hd[x];i!=0;i=e[i].nxt){
		int y=e[i].to;
		if(y==fa) continue;
		initdfs(y,x);
	}
	return ;
} 
int LCA(int x,int y){
	if(dep[y]<dep[x]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(dep[dp[y][i]]>=dep[x]) y=dp[y][i];
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--){
		if(dp[x][i]!=dp[y][i]){
			x=dp[x][i];
			y=dp[y][i];
		}
	}
	return dp[x][0];
}
int cnt[2][300005<<1];
vector<pair<int,int> > add[300005],del[300005];
int ans[300005];
void dfs(int x,int fa){
    int now0=cnt[0][dep[x]+w[x]];
    int now1=cnt[1][mov(w[x]-dep[x])];
    for(auto now:add[x]) ++cnt[now.fir][now.sec];
    for(auto now:del[x]) --cnt[now.fir][now.sec];
    for(int i=hd[x];i!=0;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x);
    }
    ans[x]=cnt[0][dep[x]+w[x]]-now0+cnt[1][mov(w[x]-dep[x])]-now1;
    return ;
}
signed main(){
    n=read(); m=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read();
        Link(u,v);
        Link(v,u);
    }
    initdfs(1,0);
    for(int i=1;i<=n;i++) w[i]=read();
    for(int i=1;i<=m;i++){
        s[i]=read(); t[i]=read();
        int qwq=LCA(s[i],t[i]);
        add[s[i]].push_back({0,dep[s[i]]});
        del[fath[qwq]].push_back({0,dep[s[i]]});
        add[t[i]].push_back({1,mov(dep[s[i]]-2*dep[qwq])});
        del[qwq].push_back({1,mov(dep[s[i]]-2*dep[qwq])});
    }
    dfs(1,0);
    for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
    printf("\n");
    return 0;
}
posted @   HAM_qwq  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示