Loj 3014 独特的城市

树的直径.

记树的某条直径为 $S-T$ ,则对于一个点 $x$ ,能对它造成贡献的点,一定都在 $x$ 到 $S,T$ 中较远的那个点的路径上.

否则,若还有其他点能对 $x$ 造成贡献,那么它到 $x$ 的距离一定大于 $x$ 到 $S,T$ 的距离,可以构造出更长的直径.

以 $S$ 为根做一次 dfs ,对每个 $x$ ,统计一下 $S\to x$ 路径上有几个点能对 $x$ 造成贡献,再以 $T$ 为根做一次同样的 dfs .

对于同一个 $x$ 的两次 dfs ,假设 $x$ 到 $S$ 更远,那么 $T$ 那一次的 dfs 计入贡献的点就是 $S$ 那一次计入贡献的点的子集.

那么把两次的答案取 $\max$ 就是实际的答案.

dfs 时,用一个栈维护根到当前节点路径中,能造成贡献的点,开一个桶记录每种权值出现次数.

往栈中加点或者删点的时候,更新桶,并且更新当前答案,类似于莫队增加或删除端点时的操作.

假设当前已经处理了 $x$ 的答案,需要继续向下 dfs .

为了保证栈中点都是有贡献的,要先把栈中与 $x$ 距离小于等于 $x$ 到其它子树中最深的点的所有点删掉.

先往最深点的方向走,再往其它儿子走,删除操作就不用被撤销掉了.

每个点只会对它的父亲贡献 $O(1)$ 次进出栈操作,时间复杂度 $O(n)$ .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//%std
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int MAXN=2e5+10;
int n,m,ecnt=0,rt,dep[MAXN],c[MAXN];
vector<int> G[MAXN];
int bucket[MAXN],ans[MAXN],tmp=0;
void ins(int x)
{
tmp+=!(bucket[x]++);
}
void del(int x)
{
tmp-=!(--bucket[x]);
}
void FindRt(int u,int fa)
{
if(dep[u]>dep[rt])
rt=u;
for(int v:G[u])
if(v!=fa)
dep[v]=dep[u]+1,FindRt(v,u);
}
int mxson[MAXN],mx[MAXN],se[MAXN];
void Init(int u,int fa)
{
mxson[u]=0;
mx[u]=dep[u],se[u]=-1;
for(int v:G[u])
{
if(v==fa)
continue;
dep[v]=dep[u]+1;
Init(v,u);
if(mx[v]>mx[mxson[u]])
mxson[u]=v,mx[u]=mx[v];
}
for(int v:G[u])
if(v!=fa && v!=mxson[u])
se[u]=max(se[u],mx[v]);
}
int stk[MAXN],tp=0;
void dfs(int u,int fa)
{
while(tp && dep[u]-dep[stk[tp]]<=se[u]-dep[u])
del(c[stk[tp--]]);
ins(c[stk[++tp]=u]);
if(mxson[u])
dfs(mxson[u],u);
if(tp && stk[tp]==u)
del(c[stk[tp--]]);
while(tp && dep[u]-dep[stk[tp]]<=mx[u]-dep[u])
del(c[stk[tp--]]);
ans[u]=max(ans[u],tmp);
for(int v:G[u])
{
if(v==fa || v==mxson[u])
continue;
if(!tp || stk[tp]!=u)
ins(c[stk[++tp]=u]);
dfs(v,u);
}
if(tp && stk[tp]==u)
del(c[stk[tp--]]);
}
int main()
{
n=read(),m=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
G[u].push_back(v);
G[v].push_back(u);
}
for(int i=1;i<=n;++i)
c[i]=read();
mx[0]=dep[0]=-1;
int S,T;
FindRt(1,0),S=rt,FindRt(S,0),T=rt;
tp=dep[S]=0,Init(S,0),dfs(S,0);
tp=dep[T]=0,Init(T,0),dfs(T,0);
for(int i=1;i<=n;++i)
printf("%d\n",ans[i]);
return 0;
}