zoukankan      html  css  js  c++  java
  • HDU 5977 Garden of Eden (点分治+子集枚举)

    题意:给你一颗树,每个节点上都有一个种类,问你包含所有种类的路径有多少条(1,3)(3,1)算2条

    思路:最近狂补点分治,这道题点分治的部分还是很好理解的,直接套点分治的板子,只不过这道题不需要使用dep数组,我们知道dep其实维护的是从rt到子节点的权值,这里因为只有10种种类,所以我们直接状压(k<10,明示状压),然后就是子集枚举加速遍历(好久没写过这个了,记得之前一次写是在一次状压dp里,里面用到了子集枚举加速,但是题目已经记不清楚了),在这里复习了一下,(附一篇网上的题解,传送门

    代码:(写完点分治忘记第一次找根的时候要对f[0]=sum=n,这条赋值,找了很久)

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    
    const int maxn=5e4+7;
    struct Edge
    {
        int to,next;
    }a[maxn<<1];
    int n,k,head[maxn],cnt;
    int root,sum,vis[maxn],sz[maxn];
    int f[maxn],stk[maxn],top;
    LL ans;
    LL Hash[1200];
    int col[maxn];
    void addedge(int u,int v)
    {
        a[++cnt].to=v;
        a[cnt].next=head[u];
        head[u]=cnt;
    }
    void init()
    {
        cnt=0;
        memset(head,0,sizeof(head));
        for(int i=0;i<=n+3;++i)vis[i]=0;
        ans=0;root=0;
    }
    void getroot(int u,int fa)
    {
        sz[u]=1;f[u]=0;
        for(int e=head[u];e;e=a[e].next){
            int v=a[e].to;
            if(v==fa||vis[v])continue;
            getroot(v,u);
            sz[u]+=sz[v];
            f[u]=max(f[u],sz[v]);
        }
        f[u]=max(f[u],sum-sz[u]);
        if(f[u]<f[root])root=u;
    }
    
    void getdep(int u,int fa,int s)
    {
        stk[++top]=s;
        for(int e=head[u];e;e=a[e].next){
            int v=a[e].to;
            if(v==fa||vis[v])continue;
            getdep(v,u,s|(1<<col[v]));
        }
    }
    LL calc(int u,int s)
    {
        top=0;
        getdep(u,0,s);
        LL res=0;
        memset(Hash,0,sizeof(Hash));
        for(int i=1;i<=top;i++)Hash[stk[i]]++;
        for(int i=1;i<=top;i++){
            Hash[stk[i]]--;
            res+=Hash[(1<<k)-1];
            for(int s0=stk[i];s0;s0=(s0-1)&stk[i]){
                res+=Hash[((1<<k)-1)^s0];
            }
            Hash[stk[i]]++;
        }
        return res;
    }
    
    void solve(int u)
    {
        ans+=calc(u,(1<<col[u]));
        vis[u]=1;
        for(int e=head[u];e;e=a[e].next){
            int v=a[e].to;
            if(vis[v])continue;
            ans-=calc(v,(1<<col[u])|(1<<col[v]));
            sum=sz[v];root=0;
            getroot(v,0);
            solve(root);
        }
    }
    
    int main()
    {
        while(~scanf("%d%d",&n,&k)){
            init();
            for(int i=1;i<=n;++i){
                scanf("%d",&col[i]);
                col[i]--;
            }
            for(int i=1;i<n;++i){
                int u,v;
                scanf("%d%d",&u,&v);
                addedge(u,v);
                addedge(v,u);
            }
            if(k==1){
                printf("%d
    ",n*n);
                continue;
            }
            ans=0;
            sum=f[0]=n;
            getroot(1,0);
            solve(root);
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    shell编程系列5--数学运算
    qperf测量网络带宽和延迟
    使用gprof对应用程序做性能评测
    [转]极不和谐的 fork 多线程程序
    Emacs显示光标在哪个函数
    Iterm2的一些好用法
    [转]最佳日志实践
    Deep Introduction to Go Interfaces.
    CGo中传递多维数组给C函数
    seaweedfs 源码笔记(一)
  • 原文地址:https://www.cnblogs.com/lalalatianlalu/p/9778124.html
Copyright © 2011-2022 走看看