并没有参加NOIP2016,不过仍然听说过这题的威名,今日一见,果然不凡操蛋
暴力分给的很足,但是暴力写完了,离正解也不远了
25 分
维护一下父亲,每条路径暴力跳就好了
40 分
对于链式,我们思考一下,怎么跑才会产生贡献
对于从上往下跑的,只有
d
i
s
t
(
s
t
a
r
t
i
,
x
)
=
=
W
x
dist(start_i,x)==W_x
dist(starti,x)==Wx才可以被看到
稍微改改式子
d
e
p
x
−
d
e
p
s
t
a
r
t
i
=
=
W
x
—
—
—
—
>
d
e
p
s
t
a
r
t
i
=
=
d
e
p
x
−
W
x
dep_x-dep_{start_i}==W_x————>dep_{start_i}==dep_x-Wx
depx−depstarti==Wx————>depstarti==depx−Wx
那么我们建立一个桶,遇到路径头就
B
[
d
e
p
x
−
W
x
]
+
+
B[dep_x-W_x]++
B[depx−Wx]++,遇到路径尾就
B
[
d
e
p
X
−
W
x
]
−
−
B[dep_X-W_x]--
B[depX−Wx]−−,这样从上往下的路径就好了,那么从下往上呢,稍微推一推式子,就可以发现,这是产生贡献的东西为
d
e
p
x
+
W
x
dep_x+W_x
depx+Wx,我们我们遇到路径尾
+
+
++
++,再在路径头
−
−
--
−−,就可以解决这个问题
60 分
这个部分头都是路径头都是1,也就是根,那么直接统计,对于每个路径,在树上查分,如果这个观察员的 d e p x = = W x + 1 dep_x==W_x+1 depx==Wx+1,答案就是差分统计的值,不然就是0
80 分
这里牵扯到了一个和正解有关的思想
我们思考一下,仍然使用桶的思想,但是会不会出现不合法的情况呢
如果两个点(点A,点B)在一棵子树中,他俩的父亲是起点,路径为
F
A
—
—
>
A
F_A——>A
FA——>A,但是如果数据巧妙,
d
e
p
x
+
W
x
dep_x+W_x
depx+Wx也有可能满足统计的情况,怎么办呢,我们从下往上统计,遇到路径尾就往桶里面扔,遇到头就退掉,这样,在return回这个点的时候,我们产生的差值就是这个点被经过的贡献,正解也用到了这个思想。
100 分
终于到了满分思想
首先,我们把路径拆成两条,一个是
u
—
—
>
l
c
a
u——>lca
u——>lca,一个是
l
c
a
—
—
>
v
lca——>v
lca——>v,对于往上的一条,我们使用一个桶维护,详情见40分思路,对于向下的,我们使用另外一个桶,按照80分的思路维护,就可以得出结果
思路已经结束,但实际过程中会有很多问题
Q1:
统计两次LCA导致答案变大
A1:
因为路径拆成了两条,所以统计两次LCA是有可能的,我们思考一下何种情况会产生
当且仅当
d
e
p
l
c
a
u
,
v
+
W
l
c
a
u
,
v
=
=
d
e
p
u
dep_{lca_{u,v}}+W_{lca_{u,v}}==dep_u
deplcau,v+Wlcau,v==depu,这种情况下,往上的一条,该路径条件符合,会统计一次,在往下的一条,该点仍然符合条件,会被重复统计一次,这种情况,我们只需要在读入路径的时候先减一即可
Q2:
桶2负下标问题
A2:
这个直接平移数组即可
Q3
一个点是多条路径的起点(终点)怎么办
A3
我们使用链式前向星的方式,存起来就行
说了这么多,代码仍然不怎么好写,细节看代码吧
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 3e5+7;
const int N = 25;
struct node
{
int to,next;
}edge1[maxn*2],edge2[maxn*2],edge3[maxn*2];
int cnt1,cnt2,cnt3,head1[maxn],head2[maxn],head3[maxn],ans[maxn];
int n,m,w[maxn],dep[maxn],dis[maxn],len[maxn],be[maxn],en[maxn];
int sum[maxn],b1[maxn*3],b2[maxn*3],f[maxn][N+2],mi[N+2],mov=maxn;
void add1(int from,int to)
{
edge1[++cnt1].to=to;
edge1[cnt1].next=head1[from];
head1[from]=cnt1;
}
void add2(int from,int to)
{
edge2[++cnt2].to=to;
edge2[cnt2].next=head2[from];
head2[from]=cnt2;
}
void add3(int from,int to)
{
edge3[++cnt3].to=to;
edge3[cnt3].next=head3[from];
head3[from]=cnt3;
}
void dfs1(int u)
{
dep[u]=dep[f[u][0]]+1;
for(int i=1;mi[i]<dep[u];i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=head1[u];i;i=edge1[i].next)
{
int to=edge1[i].to;
if(to==f[u][0])continue;
f[to][0]=u;
dis[to]=dis[u]+1;
dfs1(to);
}
}
int lca(int a,int b)
{
if(dep[a]>dep[b])swap(a,b);
for(int i=log(dep[b])/log(2);i>=0;i--)
{
if(dep[f[b][i]]>=dep[a])
b=f[b][i];
}
if(a==b)return a;
for(int i=log(dep[a])/log(2);i>=0;i--)
{
if(f[a][i]!=f[b][i])
{
a=f[a][i];
b=f[b][i];
}
}
return f[a][0];
}
void dfs2(int u)
{
int t1=b1[dep[u]+w[u]],t2=b2[w[u]-dep[u]+mov];
for(int i=head1[u];i;i=edge1[i].next)
{
int to=edge1[i].to;
if(to==f[u][0])continue;
dfs2(to);
}
b1[dep[u]]+=sum[u];
for(int i=head2[u];i;i=edge2[i].next)
{
int to=edge2[i].to;
b2[len[to]-dep[en[to]]+mov]++;
}
ans[u]+=b1[dep[u]+w[u]]-t1+b2[w[u]-dep[u]+mov]-t2;
for(int i=head3[u];i;i=edge3[i].next)
{
int to=edge3[i].to;
b1[dep[be[to]]]--;
b2[len[to]-dep[en[to]]+mov]--;
}
}
int main()
{
mi[0]=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=N;i++)mi[i]=mi[i-1]*2;
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add1(x,y),add1(y,x);
}
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
dfs1(1);
for(int i=1;i<=m;i++)
{
int x,y,Lca;
scanf("%d%d",&x,&y);
Lca=lca(x,y);
be[i]=x,en[i]=y;
len[i]=dis[x]+dis[y]-2*dis[Lca];
add2(y,i),add3(Lca,i);
sum[x]++;
if(dep[Lca]+w[Lca]==dep[x])ans[Lca]--;
}
dfs2(1);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
}