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;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

转载于:https://www.cnblogs.com/ljh2000-jump/p/6528402.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值