codeforces 486D

题意:

给一棵树,每个点有个权值,求最大权值-最小权值<=d的连通块个数

题解:

枚举最大权值点作为根,可以算出最小权值最小是多少,然后树形dp,每次合并两个连通块dp[u]=dp[u]+dp[u]*dp[v] (原来的方案数+加入新连通块的方案数)

树形dp中大于最大或者小于最小显然就不考虑了;

我们考虑这样计数有什么问题:两个相同权值的点当最大权值的情况,同样的连通块会算两次;

考虑怎么去重:如果v的标号>root的标号,那么就不考虑。

 1 #include<bits/stdc++.h>
 2 #define maxn 2005
 3 #define ll long long
 4 using namespace std;
 5 const ll mod = 1000000007;
 6 int n,d;
 7 int a[maxn];
 8 struct edge
 9 {
10     int to,next;
11 }e[maxn<<1];
12 int head[maxn],p;
13 void addedge(int u,int v)
14 {
15     e[++p].to=v;e[p].next=head[u];head[u]=p;
16 }
17 ll dp[maxn];
18 void dfs(int u,int f,int minv,int maxv,int rt)
19 {
20     dp[u]=1;
21     for(int i=head[u];i;i=e[i].next)
22     {
23         int v=e[i].to;
24         if(v==f)continue;
25         if(a[v]<minv)continue;
26         if(a[v]>maxv||(a[v]==maxv&&v<rt))continue;
27         dfs(v,u,minv,maxv,rt);
28         dp[u]=(dp[u]+dp[u]*dp[v])%mod;
29     }
30 }
31 int main()
32 {
33     scanf("%d%d",&d,&n);
34     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
35     for(int i=1;i<n;++i)
36     {
37         int u,v;
38         scanf("%d%d",&u,&v);
39         addedge(u,v);
40         addedge(v,u);
41     }
42     ll ans=0; 
43     for(int u=1;u<=n;++u)
44     {
45         memset(dp,0,sizeof(dp));
46         dfs(u,0,a[u]-d,a[u],u);
47         ans+=dp[u];
48         ans%=mod;
49     }
50     printf("%I64d
",ans);
51     return 0;
52 }
View Code
原文地址:https://www.cnblogs.com/uuzlove/p/10447926.html