• Codeforces 486D Valid Sets:Tree dp【n遍O(n)的dp】


    题目链接:http://codeforces.com/problemset/problem/486/D

    题意:

      给你一棵树,n个节点,每个节点的点权为a[i]。

      问你有多少个连通子图,使得子图中的max(a[i]) - min(a[i]) <= d。

      ps.连通子图的定义:

        如果一个点集V为一个连通子图,则对于任意两点a,b∈V,有a到b路径上的所有点u∈V。

    题解:

      因为要保证max(a[i]) - min(a[i]) <= d,所以可以人为地选出一个点rt作为点权最大的点。

      这样在求以rt为最大点的连通子图个数时,只用考虑点权不超过a[rt]的点。

     

      然而如果有两个点i,j的点权相同,分别以i,j作为最大点的两堆子图中会有重复。

      所以可以定义一下:当以rt作为最大点时,所有点权与a[rt]相等的点,它们的节点编号id[i]必须大于id[rt]。

      这样就能避免重复了。

      所以最终答案 = ∑(以i为最大点的连通子图个数)

      求以i为最大点的连通子图个数,只需一遍O(n)的dp就行。

      注意,当前这是一棵无根树。以下所说的“i的子树”意思是:从i出发往叶子方向的那一堆点。

      假设当前以rt作为最大点。  

      则加入连通子图的点i必须满足:

        (1)a[rt]-a[i]<=d(保证满足题目条件)

        (2)a[i]<=a[rt](保证a[rt]为最大点)

        (3)如果a[i]==a[rt] && i!=rt,则要满足rt<i(避免重复计数)

      表示状态:

        dp[i] = numbers

        表示节点i肯定要选,i的子树所构成的合法连通子图个数

      找出答案:

        每次ans += dp[rt]

      如何转移:

        dp[i] = ∏ (dp[son]+1)

      边界条件:

        对于叶子结点leaf: dp[leaf] = 1

      总复杂度O(n^2)。

    AC Code:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #include <vector>
     5 #define MAX_N 2005
     6 #define MOD 1000000007
     7 
     8 using namespace std;
     9 
    10 int n,d;
    11 int a[MAX_N];
    12 vector<int> edge[MAX_N];
    13 
    14 void read()
    15 {
    16     cin>>d>>n;
    17     for(int i=1;i<=n;i++) cin>>a[i];
    18     int x,y;
    19     for(int i=1;i<n;i++)
    20     {
    21         cin>>x>>y;
    22         edge[x].push_back(y);
    23         edge[y].push_back(x);
    24     }
    25 }
    26 
    27 long long dfs(int now,int p,int rt,int mx)
    28 {
    29     if(mx-a[now]>d || a[now]>mx || (a[now]==mx && p!=-1 && now<rt)) return 0;
    30     long long res=1;
    31     for(int i=0;i<edge[now].size();i++)
    32     {
    33         int temp=edge[now][i];
    34         if(temp!=p) res=res*(dfs(temp,now,rt,mx)+1)%MOD;
    35     }
    36     return res;
    37 }
    38 
    39 void work()
    40 {
    41     long long ans=0;
    42     for(int i=1;i<=n;i++) ans=(ans+dfs(i,-1,i,a[i]))%MOD;
    43     cout<<ans<<endl;
    44 }
    45 
    46 int main()
    47 {
    48     read();
    49     work();
    50 }
  • 相关阅读:
    Linux临时增加swap空间
    Build RPM package from source code
    svn installation
    svn merge详解
    VMware DRS概述及功能
    VMware Fault Tolerance 概述及功能
    VMware HA 特性
    VMware Storage VMotion概述及功能
    Windows1小时后关机命令
    WINDOWS2008 KMS 服务器安装及激活
  • 原文地址:https://www.cnblogs.com/Leohh/p/8274235.html
Copyright © 2020-2023  润新知