【Luogu】P2465山贼集团(树形状压DP)

  题目链接

  写了个70分暴力还挂了,第一遍提交只拿了十分……海星

  首先建虚拟节点多叉树转成二叉,然后子集枚举DP

  设g[x][i]是以x为根的子树内山贼集合i,x啥都不选也没贡献的时候的最大价值

  f[x][i]是要求的答案

  然后状压DP即可。

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cstdlib>
#define maxn 200
#define maxp 13
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    int next,to;
};

struct Pic{
    Edge edge[maxn*200];
    int head[maxn*100],num;
    Pic(){num=0;}
    void add(int from,int to){
        edge[++num]=(Edge){head[from],to};
        head[from]=num;
    }
}Old,New;

int cst[maxn][maxn];
int tot;
int mon[1<<maxp];
int dl[maxn],ndl[maxn*100];
int f[maxn*3][1<<maxp];
int g[maxn*3][1<<maxp];
int cost[maxn][1<<maxp];
int vl[1<<maxp];
int n,p;

void chan(int x,int fa){
    int now=x;
    for(int i=Old.head[x];i;i=Old.edge[i].next){
        int to=Old.edge[i].to;
        if(to==fa)    continue;
        if(ndl[now]==0||(ndl[now]==1&&dl[x]==1)){
            New.add(now,to);
            ndl[now]++;    dl[x]--;
        }
        else{
            New.add(now,++tot);
            now=tot;
            New.add(now,to);
            ndl[now]++;    dl[x]--;
        }
        chan(to,x);
    }
}

void dfs(int x,int fa){
    int lson=0,rson=0;
    f[x][0]=0;
    g[x][0]=0;
    for(int i=New.head[x];i;i=New.edge[i].next){
        int to=New.edge[i].to;
        if(to==fa)    continue;
        if(lson==0)    lson=to;
        else        rson=to;
        dfs(to,x);
    }
    if(x>n){
        if(rson==0){
            for(int i=1;i<(1<<p);++i)    f[x][i]=g[x][i]=f[lson][i];
            return;
        }
        for(int i=1;i<(1<<p);++i){
            for(int j=i;j;j=(j-1)&i)
                    g[x][i]=max(g[x][i],f[lson][j]+f[rson][i^j]);
            g[x][i]=max(g[x][i],f[lson][0]+f[rson][i]);
                f[x][i]=g[x][i];
            }
      }
    else{
        if(rson==0){
            for(int i=1;i<(1<<p);++i){
                g[x][i]=f[lson][i];
                f[x][i]=g[x][i]+mon[i];
            }
            for(int i=1;i<(1<<p);++i)
                for(int j=i;j;j=(j-1)&i)
                    f[x][i]=max(f[x][i],g[x][i^j]-cost[x][j]+mon[i]);
            return;
        }
        for(int i=1;i<(1<<p);++i){
            for(int j=i;j;j=(j-1)&i)
                    g[x][i]=max(g[x][i],f[lson][j]+f[rson][i^j]);
            g[x][i]=max(g[x][i],f[lson][0]+f[rson][i]);
                f[x][i]=g[x][i]+mon[i];
            }
        for(int i=1;i<(1<<p);++i)
            for(int j=i;j;j=(j-1)&i){
                int costx=cost[x][j],state=i^j;
                f[x][i]=max(f[x][i],g[x][state]+mon[i]-costx);
            }
            
    }
}

int main(){
    memset(f,-127/3,sizeof(f));
    memset(g,-127/3,sizeof(g));
    g[0][0]=0;
    f[0][0]=0;
    n=read(),p=read();
    tot=n;
    for(int i=1;i<n;++i){
        int from=read(),to=read();
        Old.add(from,to);
        Old.add(to,from);
        dl[from]++;    dl[to]++;
    }
    for(int i=2;i<=n;++i)    dl[i]--;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=p;++j)    cst[i][j]=read();
    int T=read();
    while(T--){
        int val=read(),cnt=read(),state=0;
        for(int i=1;i<=cnt;++i){
            int x=read();
            state|=(1<<(x-1));
        }
        vl[state]+=val;
    }
    for(int i=1;i<(1<<p);++i)
        for(int j=i;j;j=(j-1)&i)    mon[i]+=vl[j];
    for(int i=1;i<=n;++i)
        for(int j=1;j<(1<<p);++j){
            int costx=0;
            for(int k=1;k<=p;++k)
                if(j&(1<<(k-1)))    costx+=cst[i][k];
            cost[i][j]=costx;
        }
    chan(1,1);
    dfs(1,1);
    printf("%d\n",f[1][(1<<p)-1]);
}
/*
9 3

1 2
1 3
1 4
2 5
3 6
3 7
3 8
6 9

*/

 

posted @ 2018-05-02 14:53  Konoset  阅读(241)  评论(0编辑  收藏  举报