zoukankan      html  css  js  c++  java
  • codeforces600E Lomsat gelral

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

    题目链接:codeforces600E

    解法一:$O(nlog^2n)$

    正解:启发式合并

    解题报告:

      这道题求的是每个点的子树内的出现次数最大的数字的和。

      考虑启发式合并,我用$col[x]$的$map$表示$x$的子树内的每种权值的出现次数,$sum[x]$的$map$表示$x$的子树内每种出现次数的权值和。

      那么我在将儿子节点和父亲节点合并的时候只需要根据$col$的$size$,把小的往大的里面暴力合并就可以了。

      $get$了$map$的正确姿势…

    //It is made by ljh2000
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    #include <complex>
    #include <bitset>
    using namespace std;
    typedef long long LL;
    typedef long double LB;
    const double pi = acos(-1);
    const int MAXN = 100011;
    const int MAXM = 200011;
    int n,a[MAXN],ecnt,first[MAXN],to[MAXM],next[MAXM];
    LL ans[MAXN];
    map<int,int>col[MAXN];//统计每种颜色的出现次数
    map<int,LL>sum[MAXN];//统计每种出现次数的sum
    inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void dfs(int x,int fa){
    	col[x][a[x]]=1;
    	sum[x][1]=a[x];
    	int now,cc;
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa) continue;
    		dfs(v,x);
    		if(col[x].size()<col[v].size()) swap(col[x],col[v]),swap(sum[x],sum[v]);//小的往大的上面合并!!!
    		for(map<int,int>::iterator it=col[v].begin();it!=col[v].end();it++) {
    			now=it->first; cc=it->second;
    			if(col[x].count(now)>0) {
    				sum[x][col[x][now]]-=now;
    				col[x][now]+=cc;
    				sum[x][col[x][now]]+=now;
    			}
    			else col[x][now]=col[v][now],sum[x][col[x][now]]+=now;
    		}
    	}
    	map<int,LL>::iterator it=sum[x].end();
    	it--;//最大的应该是end-1,end是一个空指针...
    	ans[x]=it->second;
    }
    
    inline void work(){
    	n=getint(); for(int i=1;i<=n;i++) a[i]=getint(); int x,y;
    	for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
    	dfs(1,0);
    	for(int i=1;i<=n;i++)
    		printf("%I64d ",ans[i]);
    }
    
    int main()
    {
        work();
        return 0;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

    解法二:$O(nlogn)$

    正解:$dsu$ $on$ $tree$

    解题报告:

      学了一发$dsu$ $on$ $tree$。

      这个算法主要处理的是对于树上某一特征的不带修改子树统计问题,通常可以做到$O(nlogn)$。

      大概做法就是先链剖,然后我考虑用一个全局数组$cnt$来表示某个值的出现次数,如果是暴力的做法的话,就是每次处理一个节点时暴力把整棵子树的贡献加入,同时更新答案,做完之后暴力把整棵子树的贡献消除,递归做儿子节点。

      而$dsu$ $on$ $tree$的做法就是,每次先递归处理完轻儿子,然后再做重儿子。

      到了统计答案的时候,同样是把贡献暴力加入,但是假如这个点是他的父亲节点的重儿子,那么不消除贡献(所以加入贡献的时候注意不要加到重儿子上去了),往上走,直到某个点是父亲的轻儿子再把整棵子树的贡献消除即可。

     

    //It is made by ljh2000
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <ctime>
    #include <vector>
    #include <queue>
    #include <map>
    #include <set>
    #include <string>
    #include <complex>
    #include <bitset>
    using namespace std;
    typedef long long LL;
    typedef long double LB;
    typedef complex<double> C;
    const double pi = acos(-1);
    const int MAXN = 100011;
    const int MAXM = 200011;
    int n,a[MAXN],cnt[MAXN],ecnt,first[MAXN],to[MAXM],next[MAXM],size[MAXN],son[MAXN],Son;
    LL sum,Max,ans[MAXN];
    inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
    inline int getint(){
        int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
        if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    }
    
    inline void dfs(int x,int fa){
    	size[x]=1;
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa) continue;
    		dfs(v,x); size[x]+=size[v];
    		if(size[v]>size[son[x]]) son[x]=v;
    	}
    }
    
    inline void add(int x,int fa,int val){
    	cnt[ a[x] ]+=val;
    	if(cnt[ a[x] ]>Max) Max=cnt[ a[x] ],sum=a[x];
    	else if(cnt[ a[x] ]==Max) sum+=a[x];
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa || v==Son) continue;
    		add(v,x,val);
    	}
    }
    
    inline void solve(int x,int fa,bool T){
    	for(int i=first[x];i;i=next[i]) {
    		int v=to[i]; if(v==fa || v==son[x]) continue;
    		solve(v,x,1);
    	}
    
    	if(son[x])
    		solve(son[x],x,0),Son=son[x];
    
    	add(x,fa,1); Son=0;
    	ans[x]=sum;
    
    	if(T==1)
    		add(x,fa,-1),sum=Max=0;
    }
    
    inline void work(){
    	n=getint(); for(int i=1;i<=n;i++) a[i]=getint();
    	int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
    	dfs(1,0);
    	solve(1,0,1);
    	for(int i=1;i<=n;i++)
    		printf("%I64d ",ans[i]);
    }
    
    int main()
    {
        work();
        return 0;
    }
    //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
    

      

  • 相关阅读:
    实验四
    实验三 进程调度模拟程序
    实验二 调度
    一个完整的大作业
    数据结构化与保存
    爬取新闻列表
    用requests库和BeautifulSoup4库爬取新闻列表
    中文词频统计及词云制作
    组合数据类型练习,英文词频统计实例
    字符串操作练习:星座、凯撒密码、99乘法表、词频统计预处理
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6528402.html
Copyright © 2011-2022 走看看