(模板)hdoj5977 Garden of Eden(点分治)

Posted frankchen831x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(模板)hdoj5977 Garden of Eden(点分治)相关的知识,希望对你有一定的参考价值。

题目链接:https://vjudge.net/problem/HDU-5977

题意:给一颗树,每个结点上有一个权值a[i],a[i]<=10,求有多少条路径满足这条路径上所有权值的结点都出现了。

思路:

  首先利用二进制的思想,将a[i]转化为1<<(a[i]-1)。我们在子树中,计算出结点到重心的路径,用二进制表示,比如011表示该路径中权值3没有出现、权值1和2出现。因为k最大为10,那么我们在计算结果时把所有可能枚举一遍,也就1024,如果枚举的i和当前路径取或后=(1<<k)-1,那么该路径满足要求,加上即可。具体实现时用桶记录信息,mine[i]表示权值为i的路径的个数。

  另外,题目规定不同的路径仅当起点终点均不同,所以(1,2)和(2,1)是两个合法解,我的处理是先getdis一遍得到桶的信息,处理子结点,现将该子结点的子树的信息清除掉,即change函数,dfs之后再恢复回来。这种处理很重要,所以把这题当作模板记录一下。

  不得不说,写点分治时要非常细心,我总是半小时代码,1小时改bug,老是一些简单错误。

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long LL;
const int maxn=50005;
const int inf=0x3f3f3f3f;
struct node
    int v,nex;
edge[maxn<<1];

int n,k,cnt,head[maxn],a[maxn],sz[maxn],mson[maxn],Min,size,root;
int vis[maxn],flag;
LL ans,mine[1050];

void adde(int u,int v)
    edge[++cnt].v=v;
    edge[cnt].nex=head[u];
    head[u]=cnt;


void getroot(int u,int fa)
    sz[u]=1,mson[u]=0;
    for(int i=head[u];i;i=edge[i].nex)
        int v=edge[i].v;
        if(vis[v]||v==fa) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        mson[u]=max(mson[u],sz[v]);
    
    mson[u]=max(mson[u],size-sz[u]);
    if(mson[u]<Min) Min=mson[u],root=u;


void getdis(int u,int fa,int len)
    ++mine[len];
    for(int i=head[u];i;i=edge[i].nex)
        int v=edge[i].v;
        if(vis[v]||v==fa) continue;
        getdis(v,u,len|a[v]);
    


void change(int u,int fa,int len,int f)
    mine[len]+=f;
    for(int i=head[u];i;i=edge[i].nex)
        int v=edge[i].v;
        if(vis[v]||v==fa) continue;
        change(v,u,len|a[v],f);
    


void dfs(int u,int fa,int len)
    for(int i=0;i<(1<<k);++i)
        if((i|len)!=flag) continue;
        if(!mine[i]) continue;
        ans+=mine[i];
    
    for(int i=head[u];i;i=edge[i].nex)
        int v=edge[i].v;
        if(vis[v]||v==fa) continue;
        dfs(v,u,len|a[v]);
    


void solve(int u)
    getdis(u,0,a[u]);
    for(int i=0;i<(1<<k);++i)
        if((i|a[u])!=flag) continue;
        if(!mine[i]) continue;
        ans+=mine[i];
    
    for(int i=head[u];i;i=edge[i].nex)
        int v=edge[i].v;
        if(vis[v]) continue;
        change(v,u,a[u]|a[v],-1);
        dfs(v,u,a[u]|a[v]);    
        change(v,u,a[u]|a[v],1);
    
    memset(mine,0,sizeof(mine));


void fenzhi(int u,int ssize)
    vis[u]=1;
    solve(u);
    for(int i=head[u];i;i=edge[i].nex)
        int v=edge[i].v;
        if(vis[v]) continue;
        Min=inf,root=0;
        size=sz[v]<sz[u]?sz[v]:ssize-sz[u];
        getroot(v,0);
        fenzhi(root,size);
    


int main()
    while(~scanf("%d%d",&n,&k))
        cnt=0;
        ans=0;
        flag=(1<<k)-1;
        for(int i=0;i<=n;++i)
            head[i]=vis[i]=0;
        memset(mine,0,sizeof(mine));
        for(int i=1;i<=n;++i)
            scanf("%d",&a[i]);
            a[i]=1<<(a[i]-1);
        
        for(int i=1;i<n;++i)
            int u,v;
            scanf("%d%d",&u,&v);
            adde(u,v);
            adde(v,u);
        
        Min=inf,root=0,size=n;
        getroot(1,0);
        fenzhi(root,n);
        printf("%lld\n",ans);
    
    return 0;

 

以上是关于(模板)hdoj5977 Garden of Eden(点分治)的主要内容,如果未能解决你的问题,请参考以下文章

hdu-5977 Garden of Eden(树分治)

hdu 5977 Garden of Eden(点分治+状压dp)

HDU 5977 Garden of Eden

HDU - 5977 Garden of Eden(点分治)

HDU 5977 Garden of Eden (点分治+子集枚举)

HDU 5977 Garden of Eden (树形dp+快速沃尔什变换FWT)