luogu P1600 天天爱跑步
膜你赛T3考了这题的简化版,当场自闭。就此把此类题目总结一下。
膜你赛题意是这样的:相当于这个题最后问每个运动员被多少个观察员观察到。我们考虑把每条路径拆成向上和向下两条,本质相同,我们以向上路径举例。发现路径上满足 \(dep[S]-dep[x]=x\)(\(S\) 表示路径起点) 的 \(x\) 都可以获得 \(1\) 的贡献。我们差分一下,最后再一遍 \(dfs\) 统计答案(离线)就行了。
这个题和膜你赛题的区别呢,就是此题是一个标记对根到它产生贡献,所以最后统计答案的时候是统计子树。现在有个非常骚的操作:统计差值。由于我们提前知道了我们要求的量是什么,我们可以在 \(dfs\) 开始的时候记录这个初值,在 \(dfs\) 结束时看看这个值现在长什么样子,最后取个差就是我们想要的贡献了。这个 trick 好像以前也见过,但因为记忆力低下考场上并没有想起来什么。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
const int N=300009;
int n,m,head[N],cnt,x[N],y[N],w[N],p[N],dep[N],f[N][30],L[N],tax[N*4],ans[N];
struct Edge
{
int nxt,to;
}g[N*2];
vector <pair<int,int> > b[N];
void add(int from,int to)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
head[from]=cnt;
}
void init()
{
scanf("%d %d",&n,&m);
for (int i=1,X,Y;i<n;i++)
scanf("%d %d",&X,&Y),add(X,Y),add(Y,X);
for (int i=1;i<=n;i++)
scanf("%d",&w[i]);
for (int i=1;i<=m;i++)
scanf("%d %d",&x[i],&y[i]);
}
void dfs(int x,int fa)
{
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
dep[v]=dep[x]+1,f[v][0]=x;
dfs(v,x);
}
}
int LCA(int x,int y)
{
if(dep[x]!=dep[y])
{
if(dep[x]<dep[y])
swap(x,y);
int k=dep[x]-dep[y];
for (int i=20;i>=0;i--)
if(k>=1<<i)
k-=1<<i,x=f[x][i];
}
if(x==y) return x;
for (int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void DFS(int x,int fa)
{
int qwq=tax[p[x]];
for (int i=0;i<b[x].size();i++)
tax[b[x][i].first]+=b[x][i].second;
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
if(v==fa)
continue;
DFS(v,x);
}
ans[x]+=qwq-tax[p[x]];
}
void work()
{
dfs(1,-1);
for (int j=1;j<=20;j++)
for (int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
// for (int i=1;i<=n;i++,puts(""))
// for (int j=i;j<=n;j++)
// printf("%d ",LCA(i,j));
for (int i=1;i<=m;i++)
L[i]=LCA(x[i],y[i]);
for (int i=1;i<=n;i++)
p[i]=dep[i]+w[i];
for (int i=1;i<=m;i++)
b[x[i]].push_back(make_pair(dep[x[i]],-1)),
b[f[L[i]][0]].push_back(make_pair(dep[x[i]],1));
add(0,1);
DFS(0,-1);
for (int i=0;i<=n;i++)
p[i]=w[i]-dep[i]+N,b[i].clear();
for (int i=1;i<=m;i++)
{
int dis=dep[x[i]]-2*dep[L[i]]+N;
b[y[i]].push_back(make_pair(dis,-1)),
b[L[i]].push_back(make_pair(dis,1));
}
DFS(0,-1);
for (int i=1;i<=n;i++)
printf("%d ",ans[i]);puts("");
}
int main()
{
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!