BZOJ5072[Lydsy十月月赛] 小A的树 解题报告【树上背包/树形DP】

来源:互联网 发布:域名备案需要主机吗 编辑:程序博客网 时间:2024/06/16 07:22

Problem Statement
小A 成为了一个园艺家!他有一棵n 个节点的树(如果你不知道树是什么,请看Hint 部分)。他不小心打翻了墨水瓶,使得树的一些节点被染黑了。小A 发现这棵染黑了的树很漂亮,于是想从树中取出一个x 个点的联通子图,使得这些点中恰有y 个黑点,他想知道他的愿望能否实现。可是他太小,不会算,请你帮帮他。
解题报告
这道题可以理解为:有n个点,每个点有其点权,一些点的点权是1,一些点的点权是0,选择每一个点都有代价,代价为1,问能否用大小为x的背包装下权值为y的点。
关于树上背包,有这么一篇博客,这里我们不仅算出最多能选的价值,也算出最小能选到的价值就好了。
具体的状态:

dp[u][j+k]//以u为根节点,一个子树选k个点,其他子树选u个点的最小价值g[u][j+k]//以u为根节点,一个子树选k个点,其他子树选u个点的最大价值

转移:

dp[u][j+k]=min(dp[u][j+k],dp[u][j]+dp[v][k]),g[u][j+k]=max(g[u][j+k],g[u][j]+g[v][k]);

dp数组初值赋+inf,g数组赋0,搜索到每一个点的时候更改dp[u][1]/dp[v][1]。
代码如下:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=5000;struct edge{    int v,next;}ed[2*N+5];int T,n,q;int w[N+5],size[N+5];int dp[N+5][N+5],g[N+5][N+5];int vmax[N+5],vmin[N+5];int head[N+5],num;void build(int u,int v){    ed[++num].v=v;    ed[num].next=head[u];    head[u]=num;}void dfs(int u,int f){    size[u]=1;    dp[u][1]=w[u],g[u][1]=w[u];    for(int i=head[u];i!=-1;i=ed[i].next)    {        int v=ed[i].v;        if(v==f)continue;        dfs(v,u);        for(int j=size[u];j;j--)        for(int k=0;k<=size[v];k++)        dp[u][j+k]=min(dp[u][j+k],dp[u][j]+dp[v][k]),        g[u][j+k]=max(g[u][j+k],g[u][j]+g[v][k]);        size[u]+=size[v];    }    for(int i=1;i<=n;i++)vmin[i]=min(vmin[i],dp[u][i]),vmax[i]=max(vmax[i],g[u][i]);}void init(){    memset(head,-1,sizeof(head));num=0;    memset(dp,0x3f,sizeof(dp));    memset(vmin,0x3f,sizeof(vmin));    memset(vmax,0,sizeof(vmax));    memset(g,0,sizeof(g));}int main(){    for(scanf("%d",&T);T;T--)    {        init();        scanf("%d%d",&n,&q);        for(int i=1;i<n;i++)        {            int u,v;            scanf("%d%d",&u,&v);            build(u,v);            build(v,u);        }        for(int i=1;i<=n;i++)scanf("%d",&w[i]);        dfs(1,0);        for(int i=1;i<=q;i++)        {            int x,y;            scanf("%d%d",&x,&y);            if(y>=vmin[x]&&y<=vmax[x])printf("YES\n");            else printf("NO\n");        }        printf("\n");    }    return 0;}