[bzoj5457] 城市

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;
}
原文地址:https://www.cnblogs.com/hbyer/p/10279301.html