小A的树

小 A 成为了一个园艺家!他有一棵 n 个节点的树(如果你不知道树是什么,请 看 Hint 部分)。他不小心打翻了墨水瓶,使得树的一些节点被染黑了。 小 A 发 现这棵染黑了的树很漂亮,于是想从树中取出一个 x 个点的联通子图,使得这 些点中恰有 y 个黑点,他想知道他的愿望能否实现。 可是他太小,不会算,请你帮帮他。

可以发现同样多的黑点所在子图的大小是一段连续区间,然后做法同农夫的钱

#include<bits/stdc++.h>
using namespace std;
int t ,n,q,a[5001],sz[5001],f[5001][5001],g[5001][5001],ff[5001],gg[5001];
vector<int> e[5001];
void dfs(int u,int fa){
    int i,j,k;
    sz[u]=1;f[u][a[u]]=g[u][a[u]]=1;
    for(i=0;i<e[u].size();i++){
        int v=e[u][i];
        if(v==fa)continue;
        dfs(v,u);
        memcpy(ff,f[u],sizeof(f[u])),memcpy(gg,g[u],sizeof(g[u]));
        for(j=sz[u];j>=a[u];j--){
            for(k=sz[v];k>=a[v];k--){
                ff[j+k]=max(ff[j+k],f[u][j]+f[v][k]);
                gg[j+k]=min(gg[j+k],g[u][j]+g[v][k]);    
            }
        }
        sz[u]+=sz[v];
        for(j=a[u];j<=sz[u];j++)f[u][j]=ff[j],g[u][j]=gg[j];
    }
    for(i=0;i<=sz[u];i++)f[0][i]=max(f[0][i],f[u][i]),g[0][i]=min(g[0][i],g[u][i]);
    return ;
}
int main(){
    memset(g,0x3f,sizeof(g));
    cin>>t;
    while(t--){
        scanf("%d%d",&n,&q);
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        int i;
        for(i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            e[x].push_back(y),e[y].push_back(x);
        }
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        dfs(1,0);
        while(q--){
            int x,y;
            scanf("%d%d",&x,&y);
            if(g[0][y]<=x&&f[0][y]>=x)puts("YES");
            else puts("NO");
        } 
    }
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/zbsakioi/p/11032753.html