Codeforces 600E Lomsat gelral

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章