【XSY2410】【CF486D】有效集合(树形dp)

(Description)

给你一颗(n)个点的树,每个点有一个权值(a[i]),求出这颗树的所有满足

权值最大点的权值-权值最小点的权值(<=d)的联通子图的数目,答案对(10^9+7)取模


(Input)

第一行,两个整数(d,n)

第二行,(n)个整数(a[1]…a[n])

(3sim n+1)行,每行两个整数(x,y),表示(x)(y)间有一条边


(Output)

一行,一个整数,表示答案对(10^9+7)取模后的值


(Sample Input)

4 8
7 8 7 5 4 6 4 10
1 6
1 2
5 8
1 3
3 5
6 7
3 4


(Sample Output)

41


(HINT)

(0<=d<=2000,1<=n<=2000,1<=a[i]<=2000)


(Source)

 练习题 树1-树形DP

思路

我们可以发现(n)的范围十分的,所以我们考虑对于每个节点作为根进行(dfs)

对于每一个根(rt),我们假设这个(rt)是最小值,在(dfs)的过程中,我们要一直保证(rt)为最小值,所以不去遍历小于(rt)的值

然后我们考虑什么时候可以遍历他的儿子(v)

首先要满足我们上面提到的 (a[v]>a[rt])

接着,还要满足题目给出的条件权值最大点的权值-权值最小点的权值(<=d)

所以我们还要有一个条件: (a[v]-a[rt]<=d)

满足条件的(v)就可以对答案进行更新:(ans=(ans*(dfs(v,u,rt)+1)\%mod)\%mod)

然后快乐打完代码后

就会发现可喜可贺
在这里插入图片描述

为什么呢?

(After) (two) (years),我们会发现,有些答案会被计算两遍!?

我们分析可得:当(a[v]=a[rt])时, (v)(rt) 会重复计算两遍,所以我们要给它们一个顺序,即当(rt>v)时再统计答案,这样就可以做完啦


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2010,mod=1e9+7;;
int d,n,cnt=0;
int a[N];
int to[N<<1],nxt[N<<1],head[N];
void add(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;
}
ll dfs(int u,int fa,int rt)
{
    ll ans=1ll;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==fa)continue;
        if((a[v]>a[rt]||(a[v]==a[rt]&&rt>v))&&(a[v]-a[rt])<=d)
        {
            ans=(ans*(dfs(v,u,rt)+1)%mod)%mod;
        }
    }   
    return ans;
}
int main()
{
    scanf("%d %d",&d,&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int x,y;
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&x,&y);
        add(x,y);add(y,x);
    }
    ll output=0ll;
    for(int i=1;i<=n;i++)output=(output+dfs(i,0,i))%mod;
    printf("%lld",output);
    return 0;
}

原文地址:https://www.cnblogs.com/ShuraEye/p/11617151.html