JZOJ 5913. 林下风气

Description
里口福因有林下风气,带领全国各地高校掀起了一股AK风,大家都十分痴迷于AK。里口福为了打击大家的自信心,出了一道自以为十分困难的题目。
里口福有一棵树,第i个节点上有点权ai,他的问题就是这棵树中有多少个不同的连通块满足连通块的最大值与最小值之差=k,两个连通块不同当且仅当至少存在一个节点在一个连通块中出现而另一个连通块中没有出现。
痴迷于AK的你马上接下这道题目,在里口福狂妄的笑声中,你切掉这道题的决心更加坚定了,现在就差你的代码了。
Input
第一行两个整数n,k,表示树的大小以及题目中的k。
第二行n个整数,第i个整数表示ai。
接下来n-1行,每行两个整数x,y表示树边(x,y)。
Output
一行一个整数,表示答案,答案对19260817取模。
Sample Input
5 3
1 2 3 4 5
1 2
1 3
2 4
2 5
Sample Output
4
Data Constraint
对于30%的数据,n<=22
对于另外20%的数据,树是一条链
对于另外20%的数据,ai只有0和1两种
对于100%的数据,N<=3333,0<=ai<=N,K>=0
 
做法:有一个套路,所有差值<=k的联通块减去<=k-1的联通块即是答案,可以用树形dp统计。
 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #define mo 19260817
 5 #define N 7777
 6 #define LL long long
 7 using namespace std;
 8 int n,m,ls[N],tot;
 9 LL    ans,ans2;
10 struct edge{
11     int to,next;
12 }e[N];
13 
14 struct arr{
15     int s,num;
16 }a[N];
17 
18 void add(int x,int y){
19     e[++tot].to=y;
20     e[tot].next=ls[x];
21     ls[x]=tot;
22 }
23 
24 void Init(){
25     scanf("%d%d",&n,&m);
26     for (int i=1;i<=n;i++) scanf("%d",&a[i].s),a[i].num=i;
27     for (int i=1;i<n;i++){
28         int u,v;
29         scanf("%d%d",&u,&v);
30         add(u,v);
31         add(v,u);
32     }
33 }
34 
35 LL count(int x,int pre,int root){
36     LL sum=1;
37     for (int i=ls[x];i;i=e[i].next){
38         int v=e[i].to;
39         if (v==pre) continue;
40         if (a[root].s-a[v].s>m||a[root].s<a[v].s) continue;
41         if (a[root].s==a[v].s&&a[root].num<a[v].num) continue;
42         sum=(sum*(count(v,x,root)+1))%mo;
43     }
44     return sum;
45 }
46 
47 
48 LL count2(int x,int pre,int root){
49     LL sum=1;
50     for (int i=ls[x];i;i=e[i].next){
51         int v=e[i].to;
52         if (v==pre) continue;
53         if (a[root].s-a[v].s>m-1||a[root].s<a[v].s) continue;
54         if (a[root].s==a[v].s&&a[root].num<a[v].num) continue;
55         sum=(sum*(count2(v,x,root)+1))%mo;
56     }
57     return sum;
58 }
59 
60 
61 int main(){
62     freopen("lkf.in","r",stdin);
63     freopen("lkf.out","w",stdout);
64     Init();
65     for (int i=1;i<=n;i++)
66         ans=(ans+count(i,0,i))%mo;
67     if (m!=0){
68         for (int i=1;i<=n;i++)
69             ans2=(ans2+count2(i,0,i))%mo;
70     }
71     cout<<(ans-ans2+mo)%mo;
72 }
View Code

 

转载于:https://www.cnblogs.com/traveller-ly/p/9818488.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值