• 【bzoj5072】[Lydsy十月月赛]小A的树 树形背包dp


    题目描述

    给出一棵n个点的树,每个点有黑白两种颜色。q次询问,每次询问给出x和y,问能否选出一个x个点的联通子图,使得其中黑点数目为y。

    输入

    第一行一个正整数 T 表示数据组数。
    对于每一组数据,第一行有两个用空格隔开的正整数,分别是 n 和 q ,表示树的节点数和询问次数。
    接下来 n-1 行,每行两个用空格隔开的正整数和,表示和间有一条边相连。
    接下来一行有 n 个用空格隔开的整数,其中若,则表示第 i 个点为白色,否则为黑色。
    接下来 q 行,每行两个用空格隔开的整数 x 和 y 。
    n<=5000,q<=10^5

    输出

    对于每一组数据,输出 q 行,每行为 “YES” 或者 “NO” (不含双引号),表示对于给定的和,能否满足小A 的要求。
    每相邻两组数据的输出之间空一行。

    样例输入

    1
    9 4
    4 1
    1 5
    1 2
    3 2
    3 6
    6 7
    6 8
    9 6
    0 1 0 1 0 0 1 0 1
    3 2
    7 3
    4 0
    9 5

    输出

    YES
    YES
    NO
    NO


    题解

    树形背包dp

    (以下为考试时的想法)

    n有5000之大,直接预处理能否选出包含某数目的黑点的某大小的联通子图岂不是直接GG?

    于是大胆猜结论:~!@#¥%……&*()

    然后写了发树形背包,交上去A了。。

    (以上为考试时的想法)

    结论:.对于某一大小的连通子图,其包含黑点数的最小值与最大值之间的所有点数目都能够取得到。

    简单证明一下:考虑一个连通子图删除一个点再加入一个点后,黑点的数目变化最多只为1。因此可以变化到$[min,max]$之间所有的数目。

    然后就可以使用树形背包dp。设$f[i][j]$表示从$i$的子树中选出大小为$j$的连通子图,黑点数目的最小值;$g[i][j]$表示黑点数目的最大值。然后树形背包转移即可。

    注意树形背包的正确枚举姿势:只使用已经遍历过的点数目和当前子树中的点数目转移,否则会被链卡到$O(n^3)$。

    时间复杂度$O(Tn^2)$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 5010
    using namespace std;
    int n , head[N] , to[N << 1] , next[N << 1] , cnt , v[N] , si[N] , f[N][N] , g[N][N];
    inline void add(int x , int y)
    {
    	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
    }
    void dfs(int x , int fa)
    {
    	int i , j , k;
    	si[x] = 1 , f[x][1] = g[x][1] = v[x];
    	for(i = head[x] ; i ; i = next[i])
    	{
    		if(to[i] != fa)
    		{
    			dfs(to[i] , x);
    			for(j = si[x] ; j ; j -- )
    				for(k = si[to[i]] ; k ; k -- )
    					f[x][j + k] = min(f[x][j + k] , f[x][j] + f[to[i]][k]) , g[x][j + k] = max(g[x][j + k] , g[x][j] + g[to[i]][k]);
    			si[x] += si[to[i]];
    		}
    	}
    	for(i = 1 ; i <= n ; i ++ ) f[0][i] = min(f[0][i] , f[x][i]) , g[0][i] = max(g[0][i] , g[x][i]);
    }
    int main()
    {
    	int T;
    	scanf("%d" , &T);
    	while(T -- )
    	{
    		memset(head , 0 , sizeof(head)) , memset(f , 0x3f , sizeof(f)) , memset(g , 0xc0 , sizeof(g)) , cnt = 0;
    		int m , i , x , y;
    		scanf("%d%d" , &n , &m);
    		for(i = 1 ; i < n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);
    		for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &v[i]);
    		dfs(1 , 0);
    		for(i = 1 ; i <= m ; i ++ )
    		{
    			scanf("%d%d" , &x , &y);
    			if(y >= f[0][x] && y <= g[0][x]) puts("YES");
    			else puts("NO");
    		}
    		puts("");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Hibernate 配置双向多对多关联
    转 方法区(method) )、栈区(stack)和堆区(heap)之JVM 内存初学
    java web 实战经典(二)
    flex 生成多边形时内、外环计算
    java web 开发实战经典(一)
    sql语句联表更新(从一个数据库中的一张表更新到另一个数据库的另一张表)
    javascript DOM编程艺术(检测与性能优化)
    java数据库基本操作(sqlserver 2000为例)
    同一台电脑上配置多个解压版tomcat方法(本例安装两个)
    css那些事(一)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7760234.html
Copyright © 2020-2023  润新知