title
LUOGU 2465
题目描述
某山贼集团在绿荫村拥有强大的势力,整个绿荫村由N个连通的小村落组成,并且保证对于每两个小村落有且仅有一条简单路径相连。小村落用阿拉伯数字编号为1,2,3,4,…,n,山贼集团的总部设在编号为1的小村落中。山贼集团除了老大坐镇总部以外,其他的P个部门希望在村落的其他地方建立分部。P个分部可以在同一个小村落中建设,也可以分别建设在不同的小村落中。每个分部到总部的路径称为这个部门的管辖范围,于是这P个分部的管辖范围可能重叠,或者完全相同。在不同的村落建设不同的分部需要花费不同的费用。每个部门可能对他的管辖范围内的小村落收取保护费,但是不同的分部如果对同一小村落同时收取保护费,他们之间可能发生矛盾,从而损失一部分的利益,他们也可能相互合作,从而获取更多的利益。现在请你编写一个程序,确定P个分部的位置,使得山贼集团能够获得最大的收益。
输入输出格式
输入格式:
输入文件第一行包含一个整数N和P,表示绿荫村小村落的数量以及山贼集团的部门数量。
接下来N-1行每行包含两个整数X和Y,表示编号为X的村落与编号为Y的村落之间有一条道路相连。(1<=X,Y<=N)接下来N行,每行P个正整数,第i行第j个数表示在第i个村落建设第j个部门的分部的花费Aij。然后有一个正整数T,表示下面有T行关于山贼集团的分部门相互影响的代价。(0<=T<=2p)
最后有T行,每行最开始有一个数V,如果V为正,表示会获得额外的收益,如果V为负,则表示会损失一定的收益。然后有一个正整数C,表示本描述涉及的分部的数量,接下来有C个数,Xi,为分部门的编号(Xi不能相同)。表示如果C个分部Xi同时管辖某个小村落(可能同时存在其他分部也管辖这个小村落),可能获得的额外收益或者损失的收益为的|V|。T行中可能存在一些相同的Xi集合,表示同时存在几种收益或者损失。
输出格式:
输出文件要求第一行包含一个数Ans,表示山贼集团设置所有分部后能够获得的最大收益。
输入输出样例
输入样例#1:
2 1
1 2
2
1
1
3 1 1
输出样例#1:
5
说明
对于40%的数据,1<=P<=6。
对于100%的数据,1<=N<=100,1<=P<=12,保证答案的绝对值不超过10^8。
analysis
其实不用多叉树转二叉树,只需要跑一遍正常的树上背包就行了。树上背包的模板,详见P1273有线电视网。
我们先预处理出 数组, 代表一个节点同时被且仅被 的二进制数中是 的位的分部占领得到的收益,如 ,二进制为 ,表示被 号分部占领的收益。
为第 号村庄的子树(包括 村),内部有 状态的分部(状态的意义同 )的最大价值,初始状态为 状态的分部都建立在 村的代价。
。——独秀平川
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=110,maxp=4100;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len;
}
int n,p;
int val[maxp],f[maxn][maxp];
inline void dfs(int x,int fa)
{
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (y==fa) continue;
dfs(y,x);
for (int j=(1<<p)-1; j; --j)
for (int k=j; k; k=(k-1)&j)
f[x][j]=max(f[x][j],f[x][j^k]+f[y][k]);
}
for (int i=(1<<p)-1; i; --i)
f[x][i]+=val[i];
}
int w[maxn][13];
int main()
{
read(n);read(p);
for (int i=1; i<n; ++i)
{
int x,y;
read(x);read(y);
add(x,y);add(y,x);
}
for (int i=1; i<=n; ++i)
{
for (int j=0; j<p; ++j)
read(w[i][j]);
f[i][0]=0;
for (int j=1; j<(1<<p); ++j)
{
int s=j&-j;
int id=(log(s)+0.001)/log(2);
f[i][j]=f[i][j^s]-w[i][id];
}
}
int t;read(t);
for (int i=1; i<=t; ++i)
{
int v,c,s=0;
read(v);read(c);
for (int j=1; j<=c; ++j)
{
int x;read(x);
s|=1<<x-1;
}
val[s]+=v;
int tot=(1<<p)-1,tmp=s^tot;
for (int j=tmp; j; j=(j-1)&tmp)
val[s|j]+=v;
}
dfs(1,0);
printf("%d\n",f[1][(1<<p)-1]);
return 0;
}
多叉转二叉
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int maxn=210,maxp=4100,inf=-1061109568;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int ver[maxn],Next[maxn],head[maxn>>1],deg[maxn>>1],len;
inline void add(int x,int y)
{
ver[++len]=y,Next[len]=head[x],head[x]=len,++deg[x];
}
int son[maxn][2],id;
bool vis[maxn];
inline void dfs(int x)
{
int now=x;
vis[x]=1;
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (vis[y]) continue;
if (!son[now][0]) son[now][0]=y;
else if (deg[x]==1) son[now][1]=y;
else son[now][1]=++id,now=id,son[now][0]=y;
--deg[x],--deg[y],dfs(y);
}
}
int n,p;
int VAL[maxp],val[maxp];
int w[maxn][13],cost[maxn][maxp];
int f[maxn][maxp],g[maxn][maxp];
inline int G(int x,int s);
inline int F(int x,int s)
{
if (f[x][s]>inf) return f[x][s];
if (x>n) return G(x,s);
f[x][s]=VAL[s]-cost[x][s];
for (int i=s; i; i=(i-1)&s)
f[x][s]=max(f[x][s],G(x,i)-cost[x][i^s]+VAL[s]);
return f[x][s];
}
inline int G(int x,int s)
{
if (!son[x][0] && !son[x][1]) return inf;
if (g[x][s]>inf) return g[x][s];
if (!son[x][1])
g[x][s]=F(son[x][0],s);
else if (!son[x][0])
g[x][s]=F(son[x][1],s);
else
{
for (int i=s; i; i=(i-1)&s)
g[x][s]=max(g[x][s],F(son[x][0],i)+F(son[x][1],i^s));
g[x][s]=max(g[x][s],F(son[x][1],0)+F(son[x][1],s));
}
return g[x][s];
}
int main()
{
read(n);read(p);id=n;
for (int i=1; i<n; ++i)
{
int x,y;
read(x);read(y);
add(x,y);add(y,x);
}
for (int i=1; i<=n; ++i)
for (int j=1; j<=p; ++j)
read(w[i][j]);
int t;read(t);
for (int i=1; i<=t; ++i)
{
int v,c,s=0;read(v);read(c);
for (int j=1; j<=c; ++j)
{
int x;read(x);
s|=1<<x-1;
}
val[s]+=v;
}
dfs(1);
for (int i=0; i<(1<<p); ++i)
{
for (int j=i; j; j=(j-1)&i)
VAL[i]+=val[j];
for (int j=1; j<=n; ++j)
for (int k=1; k<=p; ++k)
if (i&(1<<k-1))
cost[j][i]+=w[j][k];
}
memset(f,192,sizeof(f));
memset(g,192,sizeof(g));
printf("%d\n",F(1,(1<<p)-1));
return 0;
}