2022年合肥市信息学市赛初中组-T1-计6器(six)
题目描述
小 C 十分喜欢数字“6”。
一天,大数据专家小 L 给了小 C 一个整数。小 C 一眼望去发现这个整数出现了很多“6”,于是想统计整数中出现了多少次“6”。
这个数字可能很大,详情请认真阅读【数据范围】。
输入格式
从文件 six.in 中读取数据。
一行,一个正整数 n,表示小 L 给小 C 的整数。
输出格式
输出到文件 six.out 中。
仅一行一个数,表示整数中“6”的个数。
输入输出样例
输入样例1:
123456
输出样例1:
1
输入样例2:
122615163618616116
输出样例2:
6
说明
【数据范围】
对于 30% 的数据:1 ≤ n ≤ 10^9;
对于 60% 的数据:1 ≤ n ≤ 10^18;
对于 100% 的数据:1 ≤ n ≤ 10^1000000。
耗时限制1000ms 内存限制512MB
解析
考点:字符串枚举
参考代码
#include <bits/stdc++.h>
using namespace std;
string s;
int ans;
int main(){
cin >> s;
for(int i=0;i<s.size();i++) {
if(s[i] == '6') ans++;
}
cout << ans;
return 0;
}
2022年合肥市信息学市赛初中组-T2-牛了个牛(ox)
题目描述
曾经一款微信小程序“羊了个羊”大火,它凭借地狱难度让无数人沉迷其中。小 C 也是如此。
“羊了个羊”中有一个牌堆,每次选择顶端的牌到槽子里,如果槽子里出现 3 张相同的牌就可以消去。
由于“羊了个羊”难度太大,且多数情况无解,于是小 C 想自己开发一款类似的游戏,叫做“牛了个牛”。
“牛了个牛”有一排牌,从 1 到 n,每张牌有个数字 ai。总共有 Q 轮游戏,每轮游戏有两个数 l、r。玩家需要将 l 到 r 的所有牌放入槽子中,如果槽子里有 2 张相同的牌即可消除,且槽子无容量限制。如果槽子里还剩卡牌,游戏失败,否则游戏胜利。注意一轮游戏结束后,无论是输是赢,n 张牌会恢复原样。
请注意两款游戏的区别。
现在小 C 让你玩“牛了个牛”,而你聪明绝顶,请回答他 Q 轮游戏的胜负情况。
输入格式
从文件 ox.in 中读取数据。
第一行两个整数 n、Q,表示牌数和游戏轮数;
第二行共 n 个数,第 i 个数为 ai,表示第 i 张牌上的数字;
接下来 Q 行,每行两个数 l 和 r,如题所述。
输出格式
输出到文件 ox.out 中。
共 Q 行,每行用 Yes、No 表示该轮游戏的胜负(第一个字母大写,后面全为小写)。
输入输出样例
输入样例1:
6 3
1 2 2 3 3 1
2 3
1 4
1 6
输出样例1:
Yes
No
Yes
输入样例2:
6 3
1 1 1 2 2 2
1 4
2 5
3 6
输出样例2:
No
Yes
No
说明
【数据范围】
对于 20% 的数据:n, Q ≤ 10;
对于 40% 的数据:n, Q ≤ 10^3;
对于另外 50% 的数据:n, Q ≤ 10^5;
其中有 40% 的数据满足:n, Q ≤ 10^5,且 ai 为 2 的幂,即 ai = 2^k,其中 k 为非负整
数;
对于所有数据:1 ≤ n, Q ≤ 10^6,1 ≤ ai ≤ n。
耗时限制1000ms 内存限制512MB
解析
考点:位运算,前缀和
1.数组标记
暴力枚举区间,查询时,用数组标记,查看区间内的数字出现的次数是否都是偶数次。由于时间复杂度O(Qn),超时,40分左右。
2.前缀异或(60分)
观察数据范围,发现 n,Q≤10^6。因此正解应是 O(n+Q) 或者 O(nlogn+Q) 的。
每次区间查询必须 O(1) 查询,因此必然预处理,由于异或运算具有 x⊗x=0 的特性,即,对于一个区间[l,r],“其上每个数字均出现偶数次” ,这个区间内的数字就可以消掉,因此考虑利用前缀异或处理。
#include<bits/stdc++.h>
using namespace std;
int n,q,l,r;
int a[1000010],x[1000010];
int main(){
scanf("%lld %lld",&n,&q);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
x[i]=x[i-1]^a[i];
}
while(q--){
scanf("%lld %lld",&l,&r);
if((x[r]^x[l-1])==0){//注意位运算的优先级低于关系运算符,要加上()
cout<<"Yes"<<endl;
}
else{
cout<<"No"<<endl;
}
}
return 0;
}
3.正解:哈希+前缀异或
问题:对于一个区间 [l,r],“其上每个数字均出现偶数次” 和 xo[r]⊗xo[l−1]=0” 是否互为充要条件?
记原数组的前缀异或数组为 xo,对于区间[l,r],如果其上每个数字均出现偶数次,那么必有 xo[r]⊗xo[l−1]=0。但是,我们能否就说 “xo[r]⊗xo[l−1]=0” 可以推出 “区间[l,r] 上每个数字均出现偶数次” ?
答案是否定的。比如,我们可以构造一个长度为 4 的区间][l,r],其上数字a,b,c,d 不是两两成对的(即 a=b,c=d 或者 a=c,b=d),且满足a⊗b⊗c⊗d=0,那么就可以使得该区间满足 xo[r]⊗xo[l−1]=0。比如:
8 2
7 23 5 21 7 38 5 36
1 4
5 8
7⊗23=16=5⊗21,7⊗38=33=5⊗36此时就会误判。
单纯的前缀异或无法解决本问题的原因:
那怎么办呢?注意到问题中的牌上的数字范围是在 [1,n] 内的,在这种情况下,我们可以轻易构造类似上面的区间元素。为什么说是“轻易构造”呢?——因为异或运算是“不带进位的加法运算”。因此,在取值范围集中在 [1,n] 上时,太容易找到数字满足三个、四个甚至更多个数字满足在这种“不带进位的加法运算”了。
比如,我们随机找一个数字 e,再随机找两个数字 a,c,即可构造 b=e⊗a,d=e⊗c 即可满足 a⊗b⊗c⊗d=0。
基于这个原因,我们甚至可以构造 5,6,7,... 个数字构成区间,并使得区间异或为 0。
如何解决这种问题?
找到问题的症结,那么就成功了一半。此时,我们要么换别的做法,要么优化现有做法,“避免”前缀异或的这个问题。既然这样的构造依赖于异或运算的“半加”性质导致的,那么我们做一个 哈希,将原本的[1,n] 区间上的数字 映射 到一个更大的区间上,来破坏其 半加/半减 性质。
至于哈希函数的选择,则任意啦,唯一的要求就是映射后的区间尽可能大,来避免出现哈希冲突和异或的冲突。比如:h(x)=2xmodp,p 为大质数即可。如果担心出现哈希冲突,多搞几个质数做哈希即可。
本题解中的做法即选用上面的哈希函数。
时间复杂度:O(nlogn),llogn 源自哈希函数的快速幂。
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5, mod = 1e9+7;
int n, q, a[N], xo[N];
int qpow(int x, int p) {
if(x == 0) return 1;
long long t = qpow(x/2, p);
return t*t*(x&1?2:1) % p;
}
int main(){
cin >> n >> q;
for(int i = 1; i <= n; i++) {
cin >> a[i];
a[i] = qpow(a[i], mod);
xo[i] = xo[i-1]^a[i];
}
while(q--) {
int l, r;
cin >> l >> r;
cout << ((xo[r]^xo[l-1])?"No":"Yes") <<'\n';
}
return 0;
}
也可以使用rand函数来完成映射
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int n,Q,s[N],b[N] ;
int main(){
freopen("ox.in","r",stdin);
freopen("ox.out","w",stdout);
for(int i=1;i<=N;i++) b[i]=rand()*rand();
cin>>n>>Q;
for(int i=1;i<=n;i++){
int a;
cin>>a;
s[i]=s[i-1]^b[a];
}
while(Q--){
int l,r;
cin>>l>>r;
if(s[r]^s[l-1]){
cout<<"No"<<endl;
}else{
cout<<"Yes"<<endl;
}
}
return 0;
}
2022年合肥市信息学市赛初中组-T3-小C爱食堂(canteen)
题目描述
小 C 每到午饭时间,总是第一个冲向食堂。
食堂有一排 n 个窗口,第 i 号窗口有一个坐标 xi,不一定满足坐标递增。
小 C 爱食堂,小朋友们也爱食堂。
食堂里总共有 m 位小朋友,第 i 位小朋友的坐标为 yi,不一定满足坐标递增 。由于避免拥挤,所以有 m ≤ n,即每位小朋友至少能找到一个窗口自己独享。
总而言之,可以将食堂看成一个数轴,窗口和小朋友看成若干个坐标点。
在小朋友的世界中,他们遵循着一套规则:从 0 时刻开始,每过 1 秒钟,每位小朋友朝向最近的、还没小朋友停留的窗口移动 1 的距离,如果到两个窗口的距离相等,选择编号最小的窗口;当一个窗口有小朋友的时候,在该处编号最小的小朋友将在此窗口停留;当小朋友停留后,就不会再移动了。
现在小 C 掌握了这套规则,他想知道每位小朋友停留的时刻以及停留的窗口,这样小 C 就能赶在小朋友到达某个窗口前抢到饭。
保证窗口两两坐标不同,不保证小朋友两两坐标不同,不保证初始时小朋友与窗口坐标不同。
输入格式
从文件 canteen.in 中读取数据。
共三行,第一行两个正整数 n 和 m,表示窗口数和小朋友个数;
第二行共 n 个整数,第 i 个数为 xi,描述了窗口的坐标;
第三行共 m 个整数,第 i 个数为 yi,描述了小朋友的坐标。
窗口和小朋友的坐标不一定满足递增。
输出格式
输出到文件 canteen.out 中。
共 m 行,每行两个整数,第 i 行的描述编号为 i 的小朋友,依次表示小朋友停留的时刻、停留的窗口的编号。
输入输出样例
输入样例1:
4 3 3 -3 9 -11 2 2 0
输出样例1:
1 1 15 3 5 2
输入样例2:
6 6 2 -1 0 3 5 -6 1 1 -1 -1 3 4
输出样例2:
1 1 9 6 0 2 1 3 0 4 1 5
说明
【样例1 解释】
0时刻时,食堂情况如图:
不难看出 1、2、3 号小朋友的目标为 1 号窗口。
1 时刻时,食堂情况如图:
根据规则,1 号小朋友将在 1 号窗口停留,2 号和 3 号小朋友重新选择 2 号窗口为目标(2 号小朋友虽然跟 3 号距离同时最短,但优先选择编号小的窗口)。
5 时刻时,食堂情况如图:
此时 3 号小朋友将在 2 号窗口停留,2 号小朋友将重新选择 3 号窗口为目标。
15 时刻时,食堂情况如图:
2 号 小朋友最终在 3 号洞口停留。
【数据范围】
对于所有数据:1 ≤ m ≤ n,| xi |, | yi | ≤ 10^9。
耗时限制1000ms 内存限制512MB
解析
考点:模拟,链表
思路:
大模拟题。在理解题意后,可以分析出,要处理以下几个细节:
- 记录时间,按照时间模拟,直到所有小朋友都占领了一个窗口。—— 外层的代码框架可以是一个
while(cnt)
- 在每个时刻,要为每个小朋友确定左边和右边距离他/她最近的窗口。—— 对每个小朋友可以开两个数组去记录其左右最近的窗口
- 对每个小朋友,要根据左右窗口的编号和距离确定小朋友移动的方向和距离。—— 上面记录了之后,这个细节就好处理了
- 对于每个窗口,如果有多个小朋友同时到达,要优先选择让编号小的朋友先占领它。—— 我们可以按编号顺序去对小朋友遍历,让小朋友先去移动,这样他们就能优先占领
- 对于已经被占领了的窗口,在后续处理时,要能够跳过(忽略)它。—— 这种逻辑,链表最适合啦
在处理移动时,可以每次移动“所有小朋友中距离其最近窗口的距离”中的最小距离。
时间复杂度:O(n+m2)
空间复杂度:O(n+m)
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3+5;
int n, m, vis[N], lt[N], rt[N]; // lt,rt: 小朋友左侧和右侧窗口
long long y[N], dis[N];
pair<long long, int> x[N]; // 窗口和小朋友 first: 坐标 second: 原始编号
int xl[N], xr[N]; // 窗口链表
long long tim[N], xid[N]; // 停留时刻和窗口编号,注意时间可能超 int 开 long long
inline int find(int p) { // 二分查找 lower_bound()
int l = 1, r = n, mid, ans = n+1;
while(l <= r) {
mid = (l+r) >> 1;
if(x[mid].first >= p) ans = mid, r = mid-1;
else l = mid+1;
}
return ans;
}
inline void del(int p) { // 链表删除
int s = xl[p], t = xr[p];
xr[s] = t; xl[t] = s;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) { cin >> x[i].first; x[i].second = i; }
for(int i = 1; i <= m; i++) cin >> y[i];
// 用链表存储窗口,方便后面删除被占的窗口
sort(x+1, x+n+1);
for(int i = 1; i <= n; i++) {
xl[i] = i - 1;
xr[i] = i + 1;
} xr[0] = 1; xl[n+1] = n;
// 两端设置两个超级远的窗口,方便后续计算最近窗口
// 注意,不可设为 2e9,否则最近窗口可能会误判
// 比如左侧为 x[0]=-20亿,y[i]=-9亿,右侧为 x[.]=9亿,则会误判 0 窗口最近
x[0].first = -2e10; x[n+1].first = 2e10;
// 初始化小朋友的左右窗口信息
long long dis_min = 2e10;
for(int i = 1; i <= m; i++) {
int t = find(y[i]);
if(t == n+1) lt[i] = n, rt[i] = n+1; // 右侧没有窗口
else if(x[t].first == y[i]) lt[i] = rt[i] = t; // 初始就在窗口上
else if(t == 1) lt[i] = 0, rt[i] = 1; // 左侧无窗口
else lt[i] = t-1, rt[i] = t; // 左右均有窗口
dis[i] = min(y[i]-x[lt[i]].first, x[rt[i]].first-y[i]);
dis_min = min(dis_min, dis[i]);
}
long long cnt = m, clock = 0; // cnt: 现有未占窗口的小朋友数量,clock:现在时间
while(cnt) {
// 开始移动,找到会占窗口的小朋友,并标记对应窗口
clock += dis_min;
for(int i = 1; i <= m; i++) {
if(xid[i]) continue; // 已占好一个窗口了,跳过
int t = (y[i]-x[lt[i]].first==dis[i]?lt[i]:rt[i]); // 编号小优先
if(x[rt[i]].first-y[i] == y[i]-x[lt[i]].first && x[rt[i]].second < x[lt[i]].second) {
t = rt[i];
}
if(dis[i] == dis_min && !vis[t]) { // 因为先遍历编号小的,因此此时可以直接占用
tim[i] = clock; xid[i] = x[t].second; // 记录下来占用的时间和窗口
vis[t] = 1;
del(t);
// cout << y[i].second << " ocu " << x[t].second << endl;
cnt--;
}
int dir = (t==lt[i]?-1:1);
y[i] += dir*dis_min; // 更新位置
}
// 更新小朋友的 lt, rt, dis
dis_min = 2e10;
for(int i = 1; i <= m; i++) {
if(xid[i]) continue; // 已占好一个窗口了,跳过
while(vis[lt[i]]) lt[i] = xl[lt[i]]; // 左右跳过已被占领的窗口
while(vis[rt[i]]) rt[i] = xr[rt[i]];
dis[i] = min(y[i]-x[lt[i]].first, x[rt[i]].first-y[i]);
dis_min = min(dis_min, dis[i]);
}
}
for(int i = 1; i <= m; i++)
cout << tim[i] << ' ' << xid[i] << endl;
return 0;
}
写法2
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1010;
const long long INF=1E12;
struct node{
long long x,no; //窗口坐标 编号
}a[N];
int n,m;
long long y[N],w[N],d[N],s,t[N],c[N]; //y[i] 第i个小朋友的位置 w数组表示小朋友想去的窗口编号 d数组表示方向
bool st[N];
bool cmp(node u,node v){ //窗口按坐标从小到大排序
return u.x<v.x;
}
//左边界,找到第一个>=k的窗口下标
int bsearch(int l,int r,long long k){
while(l<r){
int mid=(l+r)>>1;
if(a[mid].x>=k){
r=mid;
}else{
l=mid+1;
}
}
return l;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i].x,a[i].no=i;
for(int i=1;i<=m;i++) cin>>y[i];
sort(a+1,a+1+n,cmp);//窗口按坐标从小到大排序
//给右边添加一个边界
//1.后面的二分查找要找到第一个>=y[i]的窗口,如果所有窗口都在第i个小朋友的左边,就会找不到
//2.为什么要这是为1e12?// 注意,不可设为 2e9,否要比这个值大
// 比如左侧为 x[0]=-1e9,y[i]=1e9,右侧为 x[.]=2e9亿,那么y[i]离右边更近,但右边实际上是一个不存在的值
a[n+1].no=n+1,a[n+1].x=1e12;
int cnt=0; //记录找到窗口的小朋友的坐标
while(cnt<m){ //每次处理一个小朋友
//计算每个小朋友的左边和右边距离他/她最近的窗口 ,以及这些小朋友中第一个到达目标窗口的最短距离
long long dis=INF;
for(int i=1;i<=m;i++){
if(st[i]==true) continue; //已经找到窗就跳出
int u=bsearch(1,n+1,y[i]);
long long l=INF,r=INF,k;
//没越界,计算左边最近的距离
if(u-1>=1) l=y[i]-a[u-1].x;
if(u<=n) r=a[u].x-y[i]; //右边的距离
//w数组表示小朋友想去的窗口编号 d数组表示方向
if(l<r){
w[i]=u-1,k=l,d[i]=0; //0左移
}else if(l>r){
w[i]=u,k=r,d[i]=1; //1右移
}else{
if(a[u].no<a[u-1].no){
w[i]=u,k=r,d[i]=1; //k记录左边和右边距离的最小值
}else{
w[i]=u-1,k=l,d[i]=0;
}
}
dis=min(dis,k); //当前剩余小朋友第一个到达目的窗口所花费的最小时间
}
s+=dis;//每一次到达目标窗口小朋友花费的时间
int idx=0; //idx 当前一次移动确定窗口的下朋友的序号
//移动操作
for(int i=1;i<=m;i++){
if(st[i]==true) continue; //已经找到窗就跳出
if(d[i]==0)
y[i]-=dis;
else
y[i]+=dis;
if(idx==0&&y[i]==a[w[i]].x){
idx=i,t[idx]=s,c[idx]=a[w[i]].no; //t[i]第i位小朋友到达窗口所需要的时间 c[i] 第iWie小朋友停留的窗口
}
}
cnt++;
//删除被当前小朋友占用的窗口
for(int i=w[idx];i<=n;i++) a[i]=a[i+1];
st[idx]=true; //后面就不用再移动了
}
for(int i=1;i<=m;i++) cout<<t[i]<<" "<<c[i]<<endl;
return 0;
}
2022年合肥市信息学市赛初中组-T4圣诞节(christmas)
题目描述
一年一度的圣诞节快到了,小 C 要在家门口准备一棵圣诞树。
这个圣诞树可以看成一个树结构,即由 n 个点、n − 1 条边构成的一个连通图。
小 C 要在每个节点处放置一个铃铛。铃铛上写有一个 1 到 W 之间的正整数 x,其中 W 是小 C 预先给定的。
设第 i 个节点处的铃铛上的正整数为 wi,对于树上所有的边 (u, v),如果都满足 |wu − wv| ≤ k,那么称这棵圣诞树是优美的,其中 k 也是小 C 预先给定的。
现在小 C 想知道有多少种方案能得到优美的圣诞树。答案对 10^9 + 9 取模。
输入格式
从文件 christmas.in 中读取数据。
第一行三个整数,分别为 n、k、W,n 表示树的大小,k、W 如题所述;
接下来 n − 1 行,每行两个数 u、v,表示一条树边 (u, v),描述了树的形态。
输出格式
输出到文件 christmas.out 中。
仅一行,一个数表示方案数对 1000000009(= 10^9 + 9)取模的结果。
输入输出样例
输入样例1:
5 1 3 1 2 1 3 2 4 2 5
输出样例1:
103
输入样例2:
10 2 4 1 2 1 3 1 4 3 5 3 6 4 7 4 8 2 9 5 10
输出样例2:
379990
说明
耗时限制1500ms 内存限制512MB
解析
考点:动态规划,树形DP
思路:
时间复杂度:O(n2k)
由于常数较大,会超时部分点。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 820;
const int mod = 1e9+9;
int n,W,k;
vector<int> T[maxn];
int len[maxn],f[maxn][maxn*100],g[maxn][maxn*100],mid[maxn];
int low[maxn],upp[maxn];
// f weight near zero
// g weight near W
void dfs1(int now,int fa){
for(auto x:T[now]){
if(x != fa) {
dfs1(x,now);
len[now] = max(len[now],len[x]);
}
}
len[now]++;
}
int getsum(int x,int l,int r){
int sum = 0;
for(int j=l;j<=r;j++){
if(j <= low[x]) sum = sum + f[x][j];
else if(j >= upp[x]) sum = sum + g[x][j-upp[x]];
else sum = sum + mid[x];
if(sum >= mod) sum -= mod;
}
return sum;
}
void dfs2(int now,int fa){
mid[now] = 1;
for(auto x:T[now])
if(x != fa) {
dfs2(x,now);
mid[now] = 1ll*mid[now]*mid[x]%mod;
mid[now] = 1ll*mid[now]*k%mod;
}
low[now] = min(W,(len[now]-1)*k+1);
upp[now] = max(W-(len[now]-1)*k,1);
if(upp[now] <= low[now]) upp[now] = low[now]+1;
for(int i=1;i<=low[now];i++){
f[now][i] = 1;
for(auto x:T[now]){
if(x == fa) continue;
int sum = getsum(x,max(i-k,1),min(W,i+k));
f[now][i] = 1ll*f[now][i]*sum%mod;
}
}
for(int i=upp[now];i<=W;i++){
g[now][i-upp[now]] = 1;
for(auto x:T[now]){
if(x == fa) continue;
int sum = getsum(x,max(i-k,1),min(W,i+k));
g[now][i-upp[now]] = 1ll*g[now][i-upp[now]]*sum%mod;
}
}
}
int main(){
cin >> n >> k >> W;
for(int i=1;i<n;i++){
int x,y; cin >> x >> y;
T[x].push_back(y);
T[y].push_back(x);
}
dfs1(1,0);
dfs2(1,0);
int ans = 0;
for(int i=1;i<=low[1];i++){
ans = (ans + f[1][i])%mod;
}
for(int i=upp[1];i<=W;i++){
ans = (ans + g[1][i-upp[1]])%mod;
}
ans = ans + 1ll*mid[1]*(upp[1]-low[1]-1)%mod;
ans %= mod;
cout << ans << endl;
return 0;
}
DP 前缀和优化为 O(nk)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 820;
const int mod = 1e9+9;
int n,W,k;
vector<int> T[maxn];
int len[maxn],mid[maxn];
vector<int> f[maxn],g[maxn];
int low[maxn],upp[maxn];
// f weight near zero
// g weight near W
void dfs1(int now,int fa){
for(auto x:T[now]){
if(x != fa) {
dfs1(x,now);
len[now] = max(len[now],len[x]);
}
}
len[now]++;
}
int getsum(int x,int l,int r){
int sum = 0;
//Caculate near 0
int lrange = min(l,low[x]+1);
int rrange = min(r,low[x]);
sum += (f[x][rrange]-f[x][lrange-1]+mod)%mod;
if(sum >= mod) sum -= mod;
//Caculate near W
lrange = max(l,upp[x]);
rrange = max(r,upp[x]-1);
sum += (g[x][rrange-upp[x]+1]-g[x][lrange-upp[x]]+mod)%mod;
if(sum >= mod) sum -= mod;
//Caculate middle
lrange = max(low[x]+1,l);
rrange = min(upp[x]-1,r);
if(lrange > rrange) return sum;
else return (sum+1ll*(rrange-lrange+1)*mid[x]%mod)%mod;
}
void dfs2(int now,int fa){
mid[now] = 1;
for(auto x:T[now])
if(x != fa) {
dfs2(x,now);
mid[now] = 1ll*mid[now]*mid[x]%mod;
mid[now] = 1ll*mid[now]*(2*k+1)%mod;
}
low[now] = min(W,(len[now]-1)*k+1);
upp[now] = max(W-(len[now]-1)*k,1);
f[now].resize(low[now]+5);
g[now].resize(W-upp[now]+5);
if(upp[now] <= low[now]) upp[now] = low[now]+1;
for(int i=1;i<=low[now];i++){
f[now][i] = 1;
for(auto x:T[now]){
if(x == fa) continue;
int sum = getsum(x,max(i-k,1),min(W,i+k));
f[now][i] = 1ll*f[now][i]*sum%mod;
}
}
for(int i=upp[now];i<=W;i++){
g[now][i-upp[now]+1] = 1;
for(auto x:T[now]){
if(x == fa) continue;
int sum = getsum(x,max(i-k,1),min(W,i+k));
g[now][i-upp[now]+1] = 1ll*g[now][i-upp[now]+1]*sum%mod;
}
}
for(auto x:T[now]){
if(x == fa) continue;
f[x].clear();
g[x].clear();
}
//Get prefix sum
for(int i=1;i<=low[now];i++){
f[now][i] = f[now][i-1]+f[now][i];
if(f[now][i] >= mod) f[now][i] -= mod;
}
for(int i=1;i<=W-upp[now]+1;i++){
g[now][i] = g[now][i-1]+g[now][i];
if(g[now][i] >= mod) g[now][i] -= mod;
}
}
int main(){
cin >> n >> k >> W;
for(int i=1;i<n;i++){
int x,y; cin >> x >> y;
T[x].push_back(y);
T[y].push_back(x);
}
dfs1(1,0);
dfs2(1,0);
int ans = ((f[1][low[1]]+g[1][W-upp[1]+1])%mod+1ll*mid[1]*(upp[1]-low[1]-1)%mod)%mod;
cout << ans << endl;
return 0;
}
一等200,二等140