【留坑待填】CodeChef - BLACKCOM(树形dp)

题目大意:

输入一棵树,问是否存在⼀个子联通区域,大小为 S,有 B 个黑点 。多组询问,点数 N ≤ 5000

解法:

树形背包。

有个结论:如果一个大小为 S 联通块最多包含 L 个黑点,最多包含 R 个黑点,那么可以构造大小为 S ,包含 L~R 个黑点的联通块

然而并不知道这个结论哪里来的

于是设\(dp[x][s]\)表示以\(x\)为根,大小为\(s\)的子树最多/最少包含黑点数量,树形背包一下

时间复杂度\(O(n^2)\) 这我也不会证

代码:

#include <bits/stdc++.h>
#define N 5010
#define inf 99999999
using namespace std;

int head[N],mrk[N],f[N][N],g[N][N],l[N],r[N],cnt,sz[N];

struct ed{
    int v,nxt;
}e[N<<1];
void add(int u,int v){
    e[++cnt]=(ed){v,head[u]},head[u]=cnt;
    e[++cnt]=(ed){u,head[v]},head[v]=cnt;
}

void dfs(int x,int fa){
    f[x][1]=g[x][1]=mrk[x];
    sz[x]=1;
    for(int i=head[x];i;i=e[i].nxt){
        int to=e[i].v;
        if(to!=fa){
            dfs(to,x);
            for(int j=sz[x];j>=1;--j)
                for(int k=sz[to];k>=1;--k){
                    f[x][j+k]=max(f[x][j]+f[to][k],f[x][j+k]);
                    g[x][j+k]=min(g[x][j]+g[to][k],g[x][j+k]);
                }
            sz[x]+=sz[to];
        }
    }
    for(int i=1;i<=sz[x];++i){
        l[i]=min(g[x][i],l[i]);
        r[i]=max(f[x][i],r[i]);
    }
}

void solve(){
    int a,b,i,n,q,j;
    scanf("%d%d",&n,&q);
    for(i=1;i<=n;++i){
        l[i]=inf,r[i]=-inf;
        head[i]=mrk[i]=0;
        for(j=1;j<=sz[i];++j)
            f[i][j]=-inf,g[i][j]=inf;
        sz[i]=0;
    }   
    for(i=1;i<n;++i){
        scanf("%d%d",&a,&b);
        add(a,b);
    }
    for(i=1;i<=n;++i) scanf("%d",&mrk[i]);
    dfs(1,0);
    while(q--){
        scanf("%d%d",&a,&b);
        if(l[a]<=b && b<=r[a]) puts("Yes");
        else puts("No");
    }
    cnt=0;
}

int main(){
    int T;scanf("%d",&T);
    memset(f,-1,sizeof(f));
    memset(g,0x3f,sizeof(g));
    while(T--) solve();
}

猜你喜欢

转载自www.cnblogs.com/PsychicBoom/p/11094172.html