天天爱跑步
P1600 [NOIP2016 提高组] 天天爱跑步
我们首先考虑满足可以被观测到的条件。
为了方便,我们将一条路径分为两部分,一部分是往上的(包含LCA),另一部分是往下的(不包含LCA)。
对于第一部分,满足 \(d_u-d_x=w_x\rightarrow d_u=d_x+w_x\),是固有属性。
对于第二部分,满足 \(d_u-d_p+d_x-d_p=w_x\rightarrow w_x-d_x=d_u-2d_p\),也是固有属性。
又因为这个是有作用范围的,对于每个点记录一个类似于哈希表的东西,我们可以在路径上打个标记,说明这个位置有 \(d_u/d_u-2d_p\),然后查询哈希表中下标为 \(w_x\pm d_x\) 的值。
发现这是静态的,所以可以差分,对于一段 \((a,b)\)(对于第一二部分均适用),在 \(fa_a\) 上挂上一个下标处-1,\(b\) 处+1,就可以达到效果。
然后最后搜索子树权值和即可,累加遍历子树前后的差值即可(因为遍历后恰好增加了这个子树的贡献)。
这道题的差分很特别,需细细品味。
它记录的值虽然没有任何意义,但是利用的是差值计算答案。
#include<cstdio>
#include<iostream>
#include<cassert>
#include<cstring>
#include<vector>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while
typedef pair<int,int> pii;
#define x first
#define y second
const int N=300010,K=19,M=2*N;
int n,m,f[N][K],d[N],s[N*3],w[N],ans[N];
int h[N],e[M],ne[M],idx;//don't forget memset h!
vector<pii>g[N];
struct{
int a,b,p;
}q[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int x,int fa){
d[x]=d[fa]+1;
Ed{
int j=e[i];
if(j==fa)continue;
f[j][0]=x;
E(k, K-1)f[j][k]=f[f[j][k-1]][k-1];
dfs(j,x);
}
}
int lca(int a,int b){
if(d[a]>d[b])swap(a,b);
Re(i, K-1, 0)
if(d[f[b][i]]>=d[a])b=f[b][i];
if(a==b)return a;
Re(i, K-1, 0)
if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
return f[a][0];
}
void calc(int x,int sg){
int t=w[x]+sg*d[x]+M,lst=s[t];
for(auto& it:g[x])s[it.x+M]+=it.y;//由于只是累加差值,这句话放哪里都行
Ed{
int j=e[i];
if(j==f[x][0])continue;
calc(j,sg);
}
// assert(!s[t]);
ans[x]+=s[t]-lst;
}
void calc_up(){
E(i, m){
auto[a,b,p]=q[i];
int D=d[a];
g[a].push_back({D,1});
g[f[p][0]].push_back({D,-1});
}
calc(1,1);
}
void calc_down(){
E(i, n)g[i].clear();
E(i, n*3)s[i]=0;
E(i, m){
auto[a,b,p]=q[i];
int D=d[a]-2*d[p];
g[b].push_back({D,1});
g[p].push_back({D,-1});
}
calc(1,-1);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
memset(h,-1,n*4+4);
E(i, n-1){
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
dfs(1,0);
E(i, n)cin>>w[i];
E(i, m){
int a,b;
cin>>a>>b;
// cout<<a<<' '<<b<<' '<<lca(a,b)<<'\n';
q[i]={a,b,lca(a,b)};
}
calc_up();
calc_down();
E(i, n)cout<<ans[i]<<' ';
return 0;
}