zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    CruiseControl.NET与TFS结合的配置文件
    环信Restfull API dotnetSDK
    NAnt0.92版本首次在windows 8.1的机子上运行报错的问题解决
    asp.net接收ajax请求参数时为空的现象
    对接微信红包时:CA证书出错,请登录微信支付商户平台下载证书
    在打开vs解决方案时,怎样让所以打开的项目自动折叠
    使用Chrome或Fiddler抓取WebSocket包
    SVN使用教程
    禁用Resharper长代码自动换行的解决办法
    SQLServer日期格式化
  • 原文地址:https://www.cnblogs.com/hbyer/p/10279301.html
Copyright © 2011-2022 走看看