(模拟赛)树集

来源:互联网 发布:微博爬虫java 编辑:程序博客网 时间:2024/06/14 13:52

问题描述

给出一棵N个节点的树,每个节点上都附有一个权值ai。现在Ann想从中选出若干个节点,满足以下条件:

 

1. 至少选出一个节点

2. 节点之间是连通的

3. 设节点中权值最大的为ap,最小的为aq,则需要满足ap-aq不大于某个定值D。

 

Ann想知道有多少种选择的方式?结果对1,000,000,007取模即可。

 

输入格式(set.in)

第一行包含两个整数D, N,分别代表定值D与节点总数N。

第二行包含N个整数ai,分别代表每个点的权值。

接下来N-1行,每行包含两个数u, v,代表树中节点u与节点v是相连的。

 

输出格式(set.out)

一个整数,代表方案数模1,000,000,007的结果。

 

样例输入

1 4

2 1 3 2

1 2

1 3

3 4

 

样例输出

8

 

样例解释

8个选择方式为:{1}, {2}, {3}, {4}, {1, 2}, {1, 3}, {3, 4},{1, 3, 4}。

 

数据范围与约束

对于30% 的数据,1<=n<=10;

对于另外的30% 的数据,d=2000.

对于100% 的数据,0<=d<=2000, 1<=n<=2000,1<=ai<=2000.



确定一个范围,最大值-最小值小于d 这确实不好实现

换一种思路,枚举最小值,考虑每个数对答案的贡献

枚举每个点作为根节点,确定范围后用树形DP统计

采用树形DP:设F[i] 为以i 为根的子树,而且必须要取节点i 的方案数是多
少。

F[i] =Π(F[x] + 1) (x 为i 的儿子)

最终当前根节点的贡献为答案为F[root]

全部加起来就好了

细节:判重。为了避免重复,在权值一样的时候,还需要比较节点的编号

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<conio.h>#define For(i,j,k) for(int i=j;i<=k;i++)using namespace std;const int N=2000+5;const int mod=1000000007;struct node{int next;int to;}a[N*2];int h[N*2],cnt;void addvage(int x,int y){    cnt++;a[cnt].to=y;a[cnt].next=h[x];h[x]=cnt;}inline int read(){char c=getchar();while(c<'0'||c>'9')c=getchar();int x=c-'0';c=getchar();while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}return x;}int d,n;long long value[N];long long sum[N],ans,prt[N];int q;void dfs(int x){sum[x]=1;for(int i=h[x];i;i=a[i].next){int y=a[i].to;if(value[y]>value[q]+d||value[y]<value[q]||prt[x]==y)continue;if(value[y]==value[q]&&y<q)continue;prt[y]=x;dfs(y);sum[x]*=(sum[y]+1);sum[x]%=mod;}}int main(){freopen("set.in","r",stdin);freopen("set.out","w",stdout);d=read();n=read();For(i,1,n)value[i]=read();int x,y;For(i,1,n-1){x=read();y=read();addvage(x,y);addvage(y,x);}For(i,1,n){q=i;dfs(i);ans+=sum[i];ans%=mod;memset(prt,0,sizeof(prt));}cout<<ans;return 0;}