P2465山贼集团

· · 题解

其实不用多叉树转二叉树,只需要跑一遍正常的树上背包就行了。树上背包的模板,详见P1273有线电视网。

我们先预处理出val[ ]数组,val[i]代表一个节点同时被且仅被i的二进制数中是1的位的分部占领得到的收益,如i=11,二进制为1011,表示被1,2,4号分部占领的收益。

dp[i][j]为第i号村庄的子树(包括i村),内部有j状态的分部(状态的意义同val)的最大价值,初始状态为j状态的分部都建立在i村的代价。

dp[i][j]=所有以i的儿子为根的子树,包含分部状态一共为j的最大值+i号村庄对答案的贡献(即val[j],子树中所有村庄都占领了i村)。

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <math.h>
using namespace std;
typedef long long LL;
const int MAX=110;
struct edge
{
    int to,nxt;
}e[MAX*2];
int head[MAX],tot;
int n,m;
int val[4100];//4100是状态数
void addedge(int fr,int to)
{
    e[++tot].to=to;
    e[tot].nxt=head[fr];
    head[fr]=tot;
}
void addtwo(int fr,int to){addedge(fr,to);addedge(to,fr);}
int dp[MAX][4100];
void initdp()
{
    int cost[MAX][13];
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<m;j++)
        {
            scanf("%d",&cost[i][j]);
        }
        dp[i][0]=0;
        for(int j=1;j<(1<<m);j++)
        {
            int lowbit=j&(-j);
            int lowid=(log(lowbit)+0.001)/log(2);
            dp[i][j]=dp[i][j^lowbit]-cost[i][lowid];//初始化dp
        }
    }
}
void calval(int x)
{
    for(int i=1;i<=x;i++)
    {
        int v,cnt,s=0;
        scanf("%d%d",&v,&cnt);
        for(int j=1;j<=cnt;j++)//输入互相影响的村庄
        {
            int mem;
            scanf("%d",&mem);
            s|=(1<<(mem-1));//状压存储
        }
        //所有包含s的集合的val都被影响
        int maxm=(1<<m)-1;val[s]+=v;
        int tmp=s^maxm;
        for(int j=tmp;j;j=(j-1)&tmp)//枚举子集的好方法
        {
            val[(s|j)]+=v;
        }
    }
}
void dfs(int now,int fa)
{
    for(int i=head[now];i!=-1;i=e[i].nxt)//枚举儿子
    {
        int son=e[i].to;
        if(son!=fa)
        {
            dfs(son,now);
            for(int j=(1<<m)-1;j;j--)
            {
                for(int k=j;k;k=(k-1)&j)
                {
                    dp[now][j]=max(dp[now][j],dp[now][j^k]+dp[son][k]);
                    //dp[now][j]:不选此子树,dp[now][j^k]+dp[son][k]:选此子树
                }
            }
        }
    }
    for(int i=(1<<m)-1;i;i--)
    {
        dp[now][i]+=val[i];//加now的贡献
    }
}
void init()
{
    memset(head,-1,sizeof(head));tot=-1;
    memset(val,0,sizeof(val));
}
int main()
{
    cin>>n>>m;
    init();
    for(int i=1;i<n;i++)
    {
        int fr,to;
        scanf("%d%d",&fr,&to);
        addtwo(fr,to);
    }
    initdp();
    int t;scanf("%d",&t);
    calval(t);
    dfs(1,0);
    cout<<dp[1][(1<<m)-1]<<endl;
    return 0;
}