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;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
codeforces600E Lomsat gelral的更多相关文章
- [Codeforces600E] Lomsat gelral(树上启发式合并)
[Codeforces600E] Lomsat gelral(树上启发式合并) 题面 给出一棵N个点的树,求其所有子树内出现次数最多的颜色编号和.如果多种颜色出现次数相同,那么编号都要算进答案 N≤1 ...
- CodeForces600E Lomsat gelral 线段树合并
从树上启发式合并搜出来的题 然而看着好像线段树合并就能解决??? 那么就用线段树合并解决吧 维护\(max, sum\)表示值域区间中的一个数出现次数的最大值以及所有众数的和即可 复杂度\(O(n \ ...
- codeforces600E. Lomsat gelral(dsu on tree)
dsu on tree先分轻重儿子先处理轻边,再处理重儿子再加上轻儿子的答案 #include<iostream> #include<cstdio> #include<q ...
- codeforces600E Lomsat gelral【线段树合并/DSU】
第一次AC这道题,是三年前的一个下午,也许晚上也说不定.当时使用的\(DSU\) \(on\) \(tree\)算法,如今已经淡忘,再学习新的算法过程中,却与旧物重逢.生活中充满不可知会的相遇,即使重 ...
- Educational Codeforces Round 2 E. Lomsat gelral 启发式合并map
E. Lomsat gelral Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/600/prob ...
- Codeforces 600 E - Lomsat gelral
E - Lomsat gelral 思路1: 树上启发式合并 代码: #include<bits/stdc++.h> using namespace std; #define fi fir ...
- 【CF600E】 Lomsat gelral
CF600E Lomsat gelral Solution 考虑一下子树的问题,我们可以把一棵树的dfn序搞出来,那么子树就是序列上的一段连续的区间. 然后就可以莫队飞速求解了. 但是这题还有\(\T ...
- 【CodeForces】600 E. Lomsat gelral (dsu on tree)
[题目]E. Lomsat gelral [题意]给定n个点的树,1为根,每个点有一种颜色ci,一种颜色占领一棵子树当且仅当子树内没有颜色的出现次数超过它,求n个答案——每棵子树的占领颜色的编号和Σc ...
- 【CF600E】Lomsat gelral(dsu on tree)
[CF600E]Lomsat gelral(dsu on tree) 题面 洛谷 CF题面自己去找找吧. 题解 \(dsu\ on\ tree\)板子题 其实就是做子树询问的一个较快的方法. 对于子树 ...
随机推荐
- KVm中EPT逆向映射机制分析
2017-05-30 前几天简要分析了linux remap机制,虽然还有些许瑕疵,但总算大致分析的比较清楚.今天分析下EPT下的逆向映射机制.EPT具体的工作流程可参考前面博文,本文对于EPT以及其 ...
- 0701-spring cloud config-简介、Config Server开发、Config Client开发
一.概述 参看地址: https://cloud.spring.io/spring-cloud-static/Edgware.SR3/single/spring-cloud.html#_spring_ ...
- python学习笔记(二十)初识面向对象
面向对象的编程的主要思想是把构成问题的各个事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述一个事物在解决问题的过程中经历的步骤和行为.对象作为程序的基本单位,将程序和数据封装其中, ...
- C的指针疑惑:C和指针8数组
]; ]; 上面申明两个数组,不能进行以下赋值 b = a; 你不能使用赋值符把一个数组的所有元素复制给另一个数组,必须使用一个循环,每次复制一个元素 数组和指针 ]; int *b; 声明一个数组, ...
- w命令
命 令:w 功能说明:显示目前登入系统的用户信息. 语 法:w [-fhlsuV][用户名称] 补充说明:执行这项指令可得知目前登入系统的用户有那些人,以及他们正在执行的程序.单独执行w 指令会显示所 ...
- 查看电脑已经连过的wifi密码
用下面两条命令可以完成 查看当前系统已经保存的网络 netsh wlan show profiles 查看wifi指定密码 netsh wlan show profiles name="wi ...
- 07 nginx反向代理和nfs服务
作业一:nginx服务二进制安装nginx包 作为web服务修改配置文件 让配置生效,验证配置 作业二:nfs服务二进制安装nfs作为共享存储挂载在三台web的网站根目录下实现,在任意一台web上修改 ...
- hadoop开发setjar方法
屏蔽 //job.setJar("/Users/sumeng/IdeaProjects/cloudHadoop/out/artifacts/cloudHadoop_jar/cloudHado ...
- hdu3518 Boring counting
地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=3518 题目: Boring counting Time Limit: 2000/1000 MS ...
- JAVA垃圾回收机
垃圾回收基本算法 串型回收和并行回收 串行回收始终在一个CPU上执行回收操作.并行回收则将回收任务分为好几步,每步使用不同的CPU执行,这样加快了执行速度,有点像流水线作业. 并发执行和暂停应用程序 ...