gdfzoj #785 买水果(树上dp)

标签:树上dp
原题链接(其实并没有什么用)
这里写图片描述
这里写图片描述
这里写图片描述

考虑到q比较庞大(10^5级别),我们不能用单个处理询问的方法来解决,这道题的方法是用树上dp来做。我们令f[i][j][0]表示在以i点为根,有j个黑点时白点的最大数量,同理,f[i][j][1]表示在以i点为根,有j个黑点时白点的最小数量。我们不难得到转移方程:

f[x][i+j][0]=min(f[x][i][0]+f[o][j][0])
f[x][i+j][1]=max(f[x][i][1]+f[o][j][1])
注:x表示当前节点,o枚举x所有的儿子,用邻接表实现。
有一个细节问题,i,j都要倒序枚举,否则一个儿子节点会被重复计算。
时间复杂度为O(n^3+n*q),详见代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 805
#define INF 1000000000
#define add(u,v) (to[++top]=head[u],head[u]=top,w[top]=v)
#define For(x) for(int h=head[x],o=w[h];h;o=w[h=to[h]])
using namespace std;
int n,i,j,q,x,y,to[maxn],head[maxn],w[maxn],f[maxn][maxn][2],top=0; 
bool vis[maxn],c[maxn],ff;
inline void dfs(int x)
{
    vis[x]=1; f[x][c[x]][0]=f[x][c[x]][1]=1-c[x]; 
    For(x)
        if (!vis[o])
        {
            dfs(o);
            for (i=n;i>=0;i--) if (f[x][i][0]!=INF)
                for (j=n;j>=0;j--) if (f[o][j][0]!=INF)
                    f[x][i+j][0]=min(f[x][i+j][0],f[x][i][0]+f[o][j][0]);
            for (i=n;i>=0;i--) if (f[x][i][1]!=-1)
                for (j=n;j>=0;j--) if (f[o][j][1]!=-1)
                    f[x][i+j][1]=max(f[x][i+j][1],f[x][i][1]+f[o][j][1]);
        } 
}
int main()
{
    scanf("%d%d",&n,&q);
    for (i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    for (i=1;i<=n;i++) scanf("%d",&c[i]);
    for (i=1;i<=n;i++) 
        for (j=0;j<=n;j++) f[i][j][0]=INF,f[i][j][1]=-1;
    memset(vis,0,sizeof(vis));
    dfs(1);
    while (q--)
    {
        scanf("%d%d",&x,&y);
        ff=0;
        for (i=1;i<=n;i++)
            if (f[i][y][0]<=x-y&&x-y<=f[i][y][1])
            {
                ff=1; break;
            }
        if (ff) printf("Yes\n");else printf("No\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值