Description
有n座城市,m个民族。这些城市之间由n-1条道路连接形成了以城市1为根的有根树。每个城市都是某一民族的聚居地,Master知道第i个城市的民族是A_i,人数是B_i。为了维护稳定,Master需要知道某个区域内人数最多的民族。他向你提出n个询问,其中第i个询问是:求以i为根的子树内,人数最多的民族有是哪个,这个民族有多少人。如果子树内人数最多的民族有多个,输出其中编号最小的民族。
Input
共有2*n行。
第一行有两个整数n, m。
接下来n-1行,每行有两个整数u, v,表示一条连接u和v的道路。
接下来n行,第i行有两个整数A_i, B_i。
n<=400000,m<=n,1<=A_i<=m,0<=B_i<=1000。
Output
共有n行。
第i行两个整数x, y,分别表示以i为根的子树中人数最多的民族和它的人数。
Solution
(dsu~on~tree).
(dsu~on~tree)是一种处理子树信息的算法。
考虑每次处理到(x)这个点的时候,先暴力(dfs)出轻儿子,然后把轻儿子子树的影响消除,再(dfs)重儿子,最后在把当前点和轻儿子的子树的影响加上更新答案就行了。
正确性显然。
复杂度其实可以类比于启发式合并,复杂度(O(nlog n))。
证明可以考虑,对于每个点,如果被当做轻儿子的子树暴力扫了一遍,那么当前子树的总(size)会至少翻一倍,所以对于每个点最多被暴力扫到(O(log n))次。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
const int maxn = 1e6+10;
int a[maxn],b[maxn],cnt[maxn],ans,head[maxn],tot,sz[maxn],hs[maxn],n,m,ns[maxn],nr[maxn];
struct edge{int to,nxt;}e[maxn<<1];
void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}
void dfs(int x,int fa) {
sz[x]=1;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
dfs(e[i].to,x),sz[x]+=sz[e[i].to];
if(sz[hs[x]]<sz[e[i].to]) hs[x]=e[i].to;
}
}
void clear(int x,int fa) {
cnt[a[x]]=0;
for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) clear(e[i].to,x);
}
void add(int x) {
cnt[a[x]]+=b[x];
if(cnt[ans]<cnt[a[x]]||(cnt[ans]==cnt[a[x]]&&ans>a[x])) ans=a[x];
}
void get(int x,int fa) {
add(x);
for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa) get(e[i].to,x);
}
void solve(int x,int fa) {
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa&&e[i].to!=hs[x])
solve(e[i].to,x),clear(e[i].to,x);
if(hs[x]) solve(hs[x],x);add(x);
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa&&e[i].to!=hs[x]) get(e[i].to,x);
ns[x]=ans,nr[x]=cnt[ans];
}
int main() {
read(n),read(m);
for(int i=1,x,y;i<n;i++) read(x),read(y),ins(x,y);
for(int i=1;i<=n;i++) read(a[i]),read(b[i]);
dfs(1,0);solve(1,0);
for(int i=1;i<=n;i++) printf("%d %d
",ns[i],nr[i]);
return 0;
}