Codeforces 600E Lomsat gelral
树上启发式合并
题意
一棵树,每一个点有一个颜色,统计以每一个节点为根的子树中出现次数最多的颜色的编号,如果有多个颜色,统计他们的和。
思路
学习了一下dsu on the tree,安利一下良心博客1,博客2,以及CF官方解答。
算法的大致过程:
首先将树轻重链剖分,在dfs的过程中先dfs轻儿子,再dfs重儿子。
假设某一个点的儿子都已经被dfs过,统计这个点的答案。统计答案的过程中要calc当前这个点的子树,但是只calc它的轻儿子,重儿子不做。
这样的话,就需要在dfs的过程中,如果当前点是它父亲的轻儿子,做完这个点之后就将影响暴力消除;而如果这个点是它父亲的重儿子,则将这个点的影响保留。
因为重儿子比较大,所以最后统计重儿子,并保留重儿子信息,可以降低复杂度。总体复杂度
O(nlogn)
,很优秀。
另外有
O(nlog2n)
的平衡树合并,需要启发式合并。
mp[n][k]表示某节点n及其子树中,颜色k出现的次数。sum[n][k]表示某节点n及其子树中,出现k次的颜色的和。
代码
dsu on tree
#include<bits\stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int MAXN=100007;
typedef long long LL;
struct Edge
{
int to, ne;
}e[MAXN<<1];
int head[MAXN], edgenum, val[MAXN];
inline void addedge(int u, int v)
{
e[edgenum].to=v, e[edgenum].ne=head[u], head[u]=edgenum++;
e[edgenum].to=u, e[edgenum].ne=head[v], head[v]=edgenum++;
}
int hson[MAXN], sz[MAXN];
void dfs1(int n, int fa)
{
sz[n]=1;hson[n]=0;
for(int i=head[n];~i;i=e[i].ne)
{
int to=e[i].to;
if(to!=fa)
{
dfs1(to, n);
if(!hson[n]) hson[n]=to;
else if(sz[hson[n]]<sz[to]) hson[n]=to;
sz[n]+=sz[to];
}
}
}
LL res, ans[MAXN];
int mval, cnt[MAXN], hs;
void suan(int n, int fa, int v)
{
cnt[val[n]]+=v;
if(mval<cnt[val[n]]) res=(LL)val[n], mval=cnt[val[n]];
else if(mval==cnt[val[n]]) res+=(LL)val[n];
for(int i=head[n];~i;i=e[i].ne)
if(e[i].to!=fa&&e[i].to!=hs)
suan(e[i].to, n, v);
}
void dfs2(int n, int fa, int kep)
{
for(int i=head[n];~i;i=e[i].ne)
{
int to=e[i].to;
if(to!=fa&&to!=hson[n])
dfs2(to, n, 0);
}
if(hson[n]) dfs2(hson[n], n, 1), hs=hson[n];
suan(n, fa, 1);hs=0;
ans[n]=res;
if(!kep) suan(n, fa, -1), mval=res=0;
}
int main()
{
int n;
while(scanf("%d", &n)==1)
{
for(int i=1;i<=n;i++)
scanf("%d", &val[i]);
M(head, -1);edgenum=0;M(ans, 0);
for(int i=1;i<n;i++)
{
int t1, t2;scanf("%d%d", &t1, &t2);
addedge(t1, t2);
}
dfs1(1, -1);
dfs2(1, -1, 1);
for(int i=1;i<=n;i++)
printf("%lld%c", ans[i], i==n ? '\n' : ' ');
}
//system("pause");
return 0;
}
平衡树启发式合并
#include<bits\stdc++.h>
#define M(a,b) memset(a,b,sizeof(a))
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
const int MAXN=100007;
typedef long long LL;
map<int, int> mp[MAXN];
map<int, LL> sum[MAXN];
struct Edge
{
int to, ne;
}e[MAXN<<1];
int head[MAXN], edgenum;
int col[MAXN];
LL ans[MAXN];
void dfs(int n, int fa)
{
mp[n].clear();sum[n].clear();
mp[n][col[n]]=1;sum[n][1]=col[n];
for(int i=head[n];~i;i=e[i].ne)
{
int to=e[i].to;
if(to!=fa)
{
dfs(to, n);
if(mp[n].size()<mp[to].size())
swap(mp[n], mp[to]), swap(sum[n], sum[to]);//启发式
for(map<int, int>::iterator ite=mp[to].begin();ite!=mp[to].end();ite++)
{
sum[n][mp[n][ite->first]]-=ite->first;
mp[n][ite->first]+=ite->second;
sum[n][mp[n][ite->first]]+=ite->first;
}
}
}
ans[n]=sum[n].rbegin()->second;
}
int main()
{
int n;
while(scanf("%d", &n)==1)
{
M(head, -1), edgenum=0;
for(int i=1;i<=n;i++) scanf("%d", &col[i]);
for(int i=1;i<n;i++)
{
int a, b;scanf("%d%d", &a, &b);
e[edgenum].to=b, e[edgenum].ne=head[a], head[a]=edgenum++;
e[edgenum].to=a, e[edgenum].ne=head[b], head[b]=edgenum++;
}
dfs(1, -1);
for(int i=1;i<=n;i++)
{
printf("%lld%c", ans[i], i==n ? '\n' : ' ');
}
}
//system("pause");
return 0;
}
close