问题概要
-
有一棵有 N N N 个顶点的树。
-
使与顶点 x x x 具有唯一距离的顶点成为独特点。
-
对于每个顶点回答独特城市的标签类型数量。
对于 4 % 4\% 4% 的数据
对每个顶点做 DFS 或 BFS。
取一个与顶点 x x x 距离最大的顶点,设置为 D D D,独特点在 ( D , x ) (D,x) (D,x) 路径上,如果有的话,对于 ( D , x ) (D,x) (D,x) 路径,找一棵在途中分支的子树,对于分支子树的最大深度,候选独特城市消失,最后剩下的顶点都是独特点。
顺便说一下,让 D 1 D1 D1 和 D 2 D2 D2 为树的直径的端点(距离最长的两点的对)。
对于任意顶点 x x x, D 1 D1 D1 和 D 2 D2 D2 中至少有一个是离 x x x 最远的顶点,
从顶点 D 1 D1 D1 进行 DFS 以确定每个顶点 x x x 是否在 ( D 1 , x ) (D1,x) (D1,x) 路径上有独特点,对 D 2 D2 D2 执行相同的 DFS 以获得每个顶点的正确答案,
从每个子树的最大深度列可以看出 ( D , x ) (D,x) (D,x) 路径上的独特点,每次通过 DFS 中的一条边时,此列都会更改其后面的恒定元素数。
设此列为 A A A,问题是:
给定很多改变 A A A 的查询 (但只有最后一个元素),对于每个 p ( 1 ≤ p ≤ ∣ A ∣ ) , p − A p ≤ j < p p(1 \leq p \leq |A|),p-A_{p} \leq j<p p(1≤p≤∣A∣),p−Ap≤j<p,当满足的 j j j 被禁用时,不被禁止的 j j j 的集合 ∣ A ∣ |A| ∣A∣ 被设置为 S S S, S S S 元素对应一个独特点。
复杂度 O ( N 2 ) O(N^2) O(N2)。
对于 36 % 36\% 36% 的数据
只有一种类型的顶点标签,判断是否有独特点,只需判断 S S S 是否为空。
当到达 ( D , x ) (D,x) (D,x) 路径的顶点 x x x 时,保留从其他以外生长的子树的最大深度的行(即保留 A A A 结束的那一行),
让我们称之为 A ′ A' A′,可以通过比较 S S S 的最小值与从 x x x 生长的子树的最大深度来找到答案,
提前找到每个子树的最大深度,沿着边缘向下时添加到 A ′ A' A′ 末尾的是刚刚下降的子树的子树兄弟的最大深度。
S S S 的最小值是原集合或 ∣ A ′ ∣ + 1 |A'|+1 ∣A′∣+1,可以 O ( 1 ) O(1) O(1) 向下走一次,
复杂度 O ( N ) O(N) O(N)。
对于 68 % 68\% 68% 的数据
所有标签都不同,只需要知道 ∣ S ∣ |S| ∣S∣ 的大小。
对于每个 p ( 1 ≤ p ≤ ∣ A ∣ ) p(1 \leq p \leq |A|) p(1≤p≤∣A∣) 不要使用满足 p − A p ≤ j < p p-A_{p} \leq j<p p−Ap≤j<p 的 j j j,
但是,准备辅助数组 B B B,对于每个 p ( 1 ≤ p ≤ ∣ A ∣ ) , B j ← B j + 1 p(1 \leq p \leq |A|),B_{j} \gets B_{j}+1 p(1≤p≤∣A∣),Bj←Bj+1 其中 j j j 满足 p − A p ≤ j < p p-A_{p} \leq j<p p−Ap≤j<p,然后,计算 j j j 使得 B j = 0 B_{j}=0 Bj=0。
A A A 的元素修改对应B的区间加法查询,如果 B B B 的区间加法和变为 0 0 0 的元素的计数可以高速完成更好。
由于 B B B 的元素总是 0 0 0 或更多,因此只需检查 min { B } = 0 \min\{B\}=0 min{B}=0 并计算 m i n min min 的元素。
可以通过线段树来完成, O ( log N ) O(\log N) O(logN) 更新一次,
更新是 N N N 次,所以总体 O ( N log N ) O(N\log N) O(NlogN)。
对于 100 % 100\% 100% 的数据
更改 A A A 的查询(但只有最后一个元素),当涉及到顶点 x x x 时,它持有 A ′ A' A′ 从顶点 x x x 返回父节点,要保存 A A A。
题中有: “当你往下走时,添加到 A ′ A' A′ 末尾的是你刚刚下降的子树的兄弟的子树的最大深度。”
添加到 A ′ A' A′ 末尾的元素单调增加,当移动到 DFS 中的兄弟子树时,查看 A A A 末尾元素的变化,除了 p p p 之外的元素可能会从 S S S 中消失但不会增加,
当返回到父节点的顶点时,如果查看 A A A 末尾元素的变化, S S S 的元素可能会消失但不会增加。
由上可知, S S S 的元素增加的次数为 N N N 次,因此,减少次数可以通过 O ( N ) O(N) O(N) 来实现。
S S S 上的操作可以用堆栈表示,所以每次 O ( 1 ) O(1) O(1),
当 S S S 的元素增加或减少时,可以通过改变相应顶点的标签集来更新标签类型的数量。
复杂度 O ( N ) O(N) O(N)。
AC code:
#include <bits/stdc++.h>
using namespace std;
using ll=int64_t;
#define int ll
#define FOR(i,a,b) for(int i=int(a);i<int(b);i++)
#define REP(i,b) FOR(i,0,b)
#define MP make_pair
#define PB push_back
#define EB emplace_back
#define ALL(x) x.begin(),x.end()
auto& errStream=cerr;
#ifdef LOCAL
#define cerr (cerr<<"-- line "<<__LINE__<<" -- ")
#else
class CerrDummy {} cerrDummy;
template<class T>
CerrDummy& operator<<(CerrDummy&cd,const T&) {return cd;}
using charTDummy=char;
using traitsDummy=char_traits<charTDummy>;
CerrDummy& operator<<(CerrDummy&cd,basic_ostream<charTDummy,traitsDummy>&(basic_ostream<charTDummy,traitsDummy>&)) {return cd;}
#define cerr cerrDummy
#endif
#define REACH cerr<<"reached"<<endl
#define DMP(x) cerr<<#x<<":"<<x<<endl
#define ZERO(x) memset(x,0,sizeof(x))
#define ONE(x) memset(x,-1,sizeof(x))
using pi=pair<int,int>;
using vi=vector<int>;
using ld=long double;
template<class T,class U>
ostream& operator<<(ostream& os,const pair<T,U>& p) {os<<"("<<p.first<<","<<p.second<<")";return os;}
template<class T>
ostream& operator <<(ostream& os,const vector<T>& v) {os<<"{";REP(i,(int)v.size()) {if(i)os<<",";os<<v[i];}os<<"}";return os;}
inline ll read() {ll i;scanf("%" SCNd64,&i);return i;}
inline void printSpace() {printf(" ");}
inline void printEoln() {printf("\n");}
inline void print(ll x,int suc=1) {printf("%" PRId64,x);if(suc==1)printEoln();if(suc==2)printSpace();}
inline string readString() {static char buf[3341000];scanf("%s",buf);return string(buf);}
inline char* readCharArray() {
static char buf[3341000];
static int bufUsed=0;
char* ret=buf+bufUsed;
scanf("%s",ret);
bufUsed+=strlen(ret)+1;
return ret;
}
template<class T,class U>
inline void chmax(T& a,U b) {if(a<b)a=b;}
template<class T,class U>
inline void chmin(T& a,U b) {if(b<a)a=b;}
template<class T>
inline T Sq(const T& t) {return t*t;}
const ll infLL=LLONG_MAX/3;
#ifdef int
const int inf=infLL;
#else
const int inf=INT_MAX/2-100;
#endif
const int Nmax=200010;
vi tr[Nmax];
int ans[Nmax];
int col[Nmax],cnt[Nmax],curAns,stBuf[Nmax],stS;
inline void AddCol(int c) {if(cnt[c]==0)curAns++;cnt[c]++;}
inline void DelCol(int c) {cnt[c]--;if(cnt[c]==0)curAns--;}
inline void Push(int v) {stBuf[stS++]=v;AddCol(col[v]);}
inline bool Empty() {return stS==0;}
inline int Last() {return stBuf[stS-1];}
inline void Pop() {DelCol(col[Last()]);stS--;}
inline void dfs1(int v,int p,int d,vi&dist) {
dist[v]=d;
for(auto to:tr[v])if(to!=p)dfs1(to,v,d+1,dist);
}
int dep[Nmax];
inline int dfs2(int v,int p) {
int res=0;
for(auto to:tr[v])if(to!=p)chmax(res,dfs2(to,v));
return dep[v]=res+1;
}
inline void dfs3(int v,int p,const vi&dist) {
vector<pi> ch;
for(auto to:tr[v]) if(to!=p)ch.EB(dep[to],to);
if(!ch.empty()) {
swap(ch[0],*max_element(ALL(ch)));
int len=ch.size()>1?max_element(ch.begin()+1,ch.end())->first:0;
for(auto c:ch) {
int to=c.second;
while(!Empty() && dist[Last()]>=dist[v]-len) Pop();
Push(v);
dfs3(to,v,dist);
if(!Empty() && Last()==v) Pop();
chmax(len,c.first);
}
while(!Empty() && dist[Last()]>=dist[v]-len) Pop();
}
chmax(ans[v],curAns);
}
inline void Solve(int root,const vi&dist) {
assert(Empty());
dfs2(root,-1);
dfs3(root,-1,dist);
}
signed main() {
int n=read(),k=read();
int root;
REP(_,n-1) {
int a=read()-1,b=read()-1;
tr[a].PB(b);
tr[b].PB(a);
}
REP(i,n) col[i]=read()-1;
vi dist(n);
dfs1(0,-1,0,dist);
root= max_element(ALL(dist)) - dist.begin();
dfs1(root,-1,0,dist);
Solve(root,dist);
root= max_element(ALL(dist)) - dist.begin();
dfs1(root,-1,0,dist);
Solve(root,dist);
REP(i,n) print(ans[i]);
return 0;
}