zoukankan      html  css  js  c++  java
  • CodeForces 486D.Valid Sets(树形DP)

    题意:给出一个n个节点的连通图,问有多少个子集,使得子集中节点的最大值减最小值的差值不大于d。子集不得为空,子集需要是一个连通图。

    分析:第一行d,n。((0 <= d <= 2000, 1 <= n <= 2000))。第二行n个数字ai表示每个节点的权值((1 <= ai <= 2000)。)我们可以看出这个不是图,是一棵树,是树,说明可以用树形DP等方式计数,我们用dfs跑每个点作为最大值时的连通图,如果这个联通图中最大值和最小值<=d,说明可以统计起来,即(res = res + res * dfs(j, u))。可以看如下的图,每次循环就加上右边乘以左边的,我们还需要防止重复的计数,即如果一个节点和最大值相同的话,我们需要从大的节点编号走到小的节点编号,可以防止重复计数。

    //这个计数方式非常经典,可以记录一下。(点分治的一种算法就采用这种做法)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <queue>
    #include <algorithm>
    
    using namespace std;
    using LL = long long;
    const int N = 2005;
    const int mod = 1e9 + 7;
    int a[N];
    int h[N], e[N * 2], ne[N * 2], idx;
    void add(int a, int b)
    {
    	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
    }
    
    int root, val;
    int d, n;
    
    int dfs(int u, int fa)
    {
    	int res = 1;
    	for (int i = h[u]; i != -1; i = ne[i])
    	{
    		int j = e[i];
    		if (j == fa) continue;
    		if ((val > a[j] && val - a[j] <= d) || (val == a[j] && j < root))
    		{
    			res = (res + ((LL)res * dfs(j, u)) % mod) % mod;
    		}
    	}
    	return res;
    }
    
    int main()
    {		
    	scanf("%d%d", &d, &n);
    
    	for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    
    	memset(h, -1, sizeof h);
    	int u, v;
    	for (int i = 1; i < n; ++i)
    	{
    		scanf("%d%d", &u, &v);
    		add(u, v), add(v, u);
    	}
    
    	int res = 0;
    	for (int i = 1; i <= n; ++i)
    	{
    		root = i, val = a[i];
    		res = (res + dfs(i, -1)) % mod;
    	}
    
    	printf("%d
    ", res);
    
    	return 0;
    }
    
  • 相关阅读:
    2、从0开始学算法 时间/空间复杂度(如何知道你的代码性能如何?)
    1、从0开始学算法 概述
    机器学习(五):通俗易懂决策树与随机森林及代码实践
    机器学习(四):通俗理解支持向量机SVM及代码实践
    机器学习(三):理解逻辑回归及二分类、多分类代码实践
    HDU 2492 Ping pong (树状数组)
    yum更新失败
    webpack3配置rules的问题
    置顶帖汇总
    2021.07.03软件更新公告
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13321463.html
Copyright © 2011-2022 走看看