1
≤
∣
S
∣
,
∣
T
∣
≤
1000
1\le|S|,|T|\le1000
1≤∣S∣,∣T∣≤1000
题解
直接模拟
代码
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>constint N =1005;char s[N], t[N];boolcheck(char c){return c =='a'|| c =='e'|| c =='i'|| c =='o'|| c =='u';}intmain(){scanf("%s%s", s +1, t +1);if(strlen(s +1)!=strlen(t +1))returnputs("No"),0;int n =strlen(s +1);for(int i =1; i <= n; i++)if(check(s[i])^check(t[i]))returnputs("No"),0;puts("Yes");return0;}
B
题意
有
n
n
n 个数
你需要进行不超过
m
m
m 次操作,每次操作可以
(1)将一个数加一
(2)把这个数扔掉
每个数最多有
k
k
k 个操作发生在这个数上
最大化剩下的数的平均值
注:不能把所有的数都扔掉
1
≤
n
,
k
≤
1
0
5
,
1
≤
m
≤
1
0
7
1\le n,k\le10^5,1\le m\le10^7
1≤n,k≤105,1≤m≤107
题解
显然,有下面几个性质
(1)一个数不可能加一之后又被扔掉
(2)被扔掉的总是较小的数
(3)对于剩下的数,具体是哪些数加上了
1
1
1 不重要
先把这
n
n
n 个数从小到大排序
枚举扔掉了几个数(记作
i
i
i )
这样我们还能进行
min
(
k
×
(
n
−
i
)
,
m
−
i
)
\min(k\times(n-i),m-i)
min(k×(n−i),m−i) 次加一操作
直接利用前 (hou) 缀和计算平均值
代码
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}typedeflonglong ll;constint N =1e5+5;int n, k, m, a[N];double ans;
ll sum[N];intmain(){
n =read(); k =read(); m =read();for(int i =1; i <= n; i++) a[i]=read();
std::sort(a +1, a + n +1);for(int i = n; i >=1; i--) sum[i]= sum[i +1]+ a[i];for(int i =1; i <= n; i++){int cnt = m -(i -1);if(cnt <0)continue;int x = n - i +1, delta = std::min(1ll* cnt,1ll* x * k);
ans = std::max(ans,1.0*(sum[i]+ delta)/ x);}printf("%.12lf\n", ans);return0;}
C
题意
你有一条长度为
2
n
2^n
2n 的纸带
纸带上
k
k
k 个点有标记,这些标记的位置坐标给定
对于一条纸带,你把它解决掉有两种方法
(1)切成两条等长的纸带,对这两条纸带分别递归处理(纸带长度为
1
1
1 时不能进行此操作)
(2)直接把这条纸带解决掉。如果这条纸带上没有标记,则需要
A
A
A 的代价,否则需要
B
×
x
×
l
B\times x\times l
B×x×l 的代价(
x
x
x 为纸带内标记的个数,
l
l
l 为纸带长度)
求把这条长度为
2
n
2^n
2n 的纸带解决掉的最小代价
1
≤
n
≤
30
,
1
≤
k
≤
1
0
5
,
1
≤
A
,
B
≤
1
0
4
1\le n\le30,1\le k\le10^5,1\le A,B\le10^4
1≤n≤30,1≤k≤105,1≤A,B≤104
算法:线段树
考虑建一棵动态开点线段树,每个点储存该区间内的标记个数,以及解决掉的最小代价
显然有
m
i
n
c
o
s
t
(
p
)
=
min
(
m
i
n
c
o
s
t
(
l
c
)
+
m
i
n
c
o
s
t
(
r
c
)
,
B
×
s
u
m
(
p
)
×
l
e
n
(
p
)
)
mincost(p)=\min(mincost(lc)+mincost(rc),B\times sum(p)\times len(p))
mincost(p)=min(mincost(lc)+mincost(rc),B×sum(p)×len(p))
m
i
n
c
o
s
t
(
p
)
mincost(p)
mincost(p) 为解决掉区间
p
p
p 的最小代价,
l
c
lc
lc 和
r
c
rc
rc 分别为线段树上
p
p
p 的左右儿子,
s
u
m
(
p
)
sum(p)
sum(p) 为区间
p
p
p 内的标记个数,
l
e
n
(
p
)
len(p)
len(p) 为区间
p
p
p 的长度
注:如果
p
p
p 为空节点则
m
i
n
c
o
s
t
(
p
)
=
A
mincost(p)=A
mincost(p)=A
复杂度
O
(
k
n
)
O(kn)
O(kn)
代码
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}template<classT>inline T Min(const T &a,const T &b){return a < b ? a : b;}typedeflonglong ll;constint N =4e6+5;int n, k, a, b, ToT, Root;struct node
{int lc, rc, sum;
ll val;} T[N];voidupt(int l,int r,int p){
ll ls = T[p].lc ? T[T[p].lc].val : a,
rs = T[p].rc ? T[T[p].rc].val : a;
T[p].sum = T[T[p].lc].sum + T[T[p].rc].sum;
T[p].val =Min(1ll* b * T[p].sum *(r - l +1), ls + rs);}voidchange(int l,int r,int pos,int v,int&p){if(!p) p =++ToT;if(l == r){
T[p].sum += v;
T[p].val =1ll* b * T[p].sum;return;}int mid = l + r >>1;if(pos <= mid)change(l, mid, pos, v, T[p].lc);elsechange(mid +1, r, pos, v, T[p].rc);upt(l, r, p);}intmain(){
n =read(); k =read(); a =read(); b =read();while(k--)change(1,1<< n,read(),1, Root);
std::cout << T[1].val << std::endl;return0;}
D
题意
定义一个长度为偶数的字符串
s
s
s 合法,当且仅当对于每个字符
c
c
c ,都满足所有的字符
c
c
c 全部出现在
s
s
s 的左半部分或全部出现在
s
s
s 的右半部分
读入一个长度为字符串
s
s
s ,包含小写和大写英文字符
有
q
q
q 组询问,每次询问给定两个字符
c
1
c_1
c1 和
c
2
c_2
c2
求有多少个长度为
∣
s
∣
|s|
∣s∣ 的合法字符串
t
t
t 满足
(1)对于任意字符
c
c
c ,
c
c
c 在
t
t
t 中的出现次数和在
s
s
s 中的出现次数相等
(2)字符
c
1
c_1
c1 和
c
2
c_2
c2 必须同时出现在
t
t
t 的左半边或右半边
则设计 DP :
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示前
i
i
i 个字符,占用左半边
j
j
j 个位置的方案数
显然
f
[
0
]
[
0
]
=
1
f[0][0]=1
f[0][0]=1
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
c
n
t
[
i
]
]
f[i][j]=f[i-1][j]+f[i-1][j-cnt[i]]
f[i][j]=f[i−1][j]+f[i−1][j−cnt[i]]
c
n
t
[
i
]
cnt[i]
cnt[i] 为字符
i
i
i 的出现次数
如果考虑字符间的相对顺序
根据多重集的排列公式,可以考虑把状态定义改一改
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示前
i
i
i 个字符,占用左半边
j
j
j 个位置的方案数除以
j
!
j!
j! 再除以
(
s
u
m
[
i
]
−
j
)
!
(sum[i]-j)!
(sum[i]−j)! 之后的结果
s
u
m
[
i
]
sum[i]
sum[i] 为前
i
i
i 个字符的出现次数之和
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
f
[
i
−
1
]
[
j
−
c
n
t
[
i
]
]
c
n
t
[
i
]
!
f[i][j]=\frac{f[i-1][j]+f[i-1][j-cnt[i]]}{cnt[i]!}
f[i][j]=cnt[i]!f[i−1][j]+f[i−1][j−cnt[i]]
(注意细节:如果
c
n
t
[
i
]
=
0
cnt[i]=0
cnt[i]=0 则字符
i
i
i 放左侧和放右侧没有区别,所以这时候直接
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
f[i][j]=f[i-1][j]
f[i][j]=f[i−1][j] )
那么不限制两个字符在同侧时,方案数就是
f
[
最
大
字
符
]
[
m
]
×
(
m
!
)
2
f[最大字符][m]\times(m!)^2
f[最大字符][m]×(m!)2 ,其中
m
=
n
2
m=\frac n2
m=2n
然后考虑如果要限制两个字符
c
1
c_1
c1 和
c
2
c_2
c2 在同侧,我们先假设这两个字符都在左侧
把
c
1
c_1
c1 和
c
2
c_2
c2 这两个字符 ban 掉,那么我们要求的就是左侧有
m
−
c
n
t
[
c
1
]
−
c
n
t
[
c
2
]
m-cnt[c_1]-cnt[c_2]
m−cnt[c1]−cnt[c2] 个字符,右侧
m
m
m 个字符(且不能使用字符
c
1
c_1
c1 和
c
2
c_2
c2 )的方案数,除以
(
m
!
)
×
(
(
m
−
c
n
t
[
c
1
]
−
c
n
t
[
c
2
]
)
!
)
(m!)\times((m-cnt[c_1]-cnt[c_2])!)
(m!)×((m−cnt[c1]−cnt[c2])!) 得到的值
∏
c
1
+
x
c
n
t
[
c
]
c
n
t
[
c
]
!
\prod_c\frac{1+x^{cnt[c]}}{cnt[c]!}
c∏cnt[c]!1+xcnt[c]
即不超过
52
52
52 (字符集大小)个,只有
2
2
2 个非零项的多项式之积
我们现在想知道对于任意两个字符
c
1
c_1
c1 和
c
2
c_2
c2 ,这不超过
52
52
52 个多项式中去掉第
c
1
c_1
c1 和第
c
2
c_2
c2 个多项式之后的乘积
枚举字符
c
1
c_1
c1 和
c
2
c_2
c2 之后对多项式
∏
c
1
+
x
c
n
t
[
c
]
c
n
t
[
c
]
!
\prod_c\frac{1+x^{cnt[c]}}{cnt[c]!}
∏ccnt[c]!1+xcnt[c] 做除法即可。由于只有
2
2
2 个非零项,所以多项式除法可以做到
O
(
n
)
O(n)
O(n) ,复杂度
O
(
5
2
2
×
n
)
O(52^2\times n)
O(522×n) ,卡常可过
当然,我们可以发现对一个
1
+
x
k
1+x^k
1+xk 形式的多项式做除法时,次数在模
k
k
k 意义下不同的项互不影响。而我们只想知道第
m
−
c
n
t
[
c
1
]
−
c
n
t
[
c
2
]
m-cnt[c_1]-cnt[c_2]
m−cnt[c1]−cnt[c2] 次项
所以枚举
c
1
c_1
c1 ,做一遍除法之后再枚举
c
2
c_2
c2 时就只需要对与
m
−
c
n
t
[
c
1
]
−
c
n
t
[
c
2
]
m-cnt[c_1]-cnt[c_2]
m−cnt[c1]−cnt[c2] 在模
c
n
t
[
c
2
]
cnt[c_2]
cnt[c2] 同余的项进行处理,复杂度可以做到
O
(
52
n
log
n
)
O(52n\log n)
O(52nlogn) ,但是为了保证复杂度,需要把
c
n
t
[
c
2
]
cnt[c_2]
cnt[c2] 相同的一起处理,不是很好写博主就没去写
回到询问,设删掉
c
1
c_1
c1 和
c
2
c_2
c2 后多项式乘积的第
m
−
c
n
t
[
c
1
]
−
c
n
t
[
c
2
]
m-cnt[c_1]-cnt[c_2]
m−cnt[c1]−cnt[c2] 项系数为
h
[
c
1
]
[
c
2
]
h[c_1][c_2]
h[c1][c2] ,那么询问结果为
2
×
h
[
c
1
]
[
c
2
]
×
(
m
!
)
2
(
c
n
t
[
c
1
]
!
)
×
(
c
n
t
[
c
2
]
!
)
\frac{2\times h[c_1][c_2]\times(m!)^2}{(cnt[c_1]!)\times(cnt[c_2]!)}
(cnt[c1]!)×(cnt[c2]!)2×h[c1][c2]×(m!)2
注意,这里的
2
2
2 是指
c
1
c_1
c1 和
c
2
c_2
c2 可以同时出现在左侧,也可以同时出现在右侧
代码
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}constint N =1e5+5, M =52, ZZQ =1e9+7;int n, m, q, fac[N], inv[N], f[N], g[N], tmp[N], pmt[N], cnt[N], h[M][M];char s[N];intqpow(int a,int b){int res =1;while(b){if(b &1) res =1ll* res * a % ZZQ;
a =1ll* a * a % ZZQ;
b >>=1;}return res;}into(char c){if(c >='a'&& c <='z')return c -'a';return c -'A'+26;}voiddivi(int x,int dis1){for(int i =0; i <= n; i++){
g[i]= f[i];if(i > n - dis1) tmp[i]=0;}for(int i = n; i >= dis1; i--){
tmp[i - dis1]=1ll* x * g[i]% ZZQ;
g[i - dis1]-= g[i];if(g[i - dis1]<0) g[i - dis1]+= ZZQ;}for(int i =0; i <= n; i++) pmt[i]= tmp[i];}voidivid(int y,int dis2){for(int i =0; i <= n; i++){
tmp[i]= pmt[i];if(i > n - dis2) g[i]=0;}for(int i = n; i >= dis2; i--){
g[i - dis2]=1ll* y * tmp[i]% ZZQ;
tmp[i - dis2]-= tmp[i];if(tmp[i - dis2]<0) tmp[i - dis2]+= ZZQ;}}intmain(){int x, y;scanf("%s", s +1);
n =strlen(s +1);for(int i =1; i <= n; i++) cnt[o(s[i])]++;
fac[0]= inv[0]= inv[1]=1;for(int i =1; i <= n; i++) fac[i]=1ll* fac[i -1]* i % ZZQ;for(int i =2; i <= n; i++)
inv[i]=1ll*(ZZQ - ZZQ / i)* inv[ZZQ % i]% ZZQ;for(int i =2; i <= n; i++)
inv[i]=1ll* inv[i]* inv[i -1]% ZZQ;
m = n >>1;
q =read();
f[0]=1;for(int i =0; i <52; i++)if(cnt[i]){for(int j = cnt[i]; j <= n; j++)
tmp[j]=1ll* f[j - cnt[i]]* inv[cnt[i]]% ZZQ;for(int j =0; j <= n; j++) f[j]=1ll* f[j]* inv[cnt[i]]% ZZQ;for(int j = cnt[i]; j <= n; j++)
f[j]=(f[j]+ tmp[j])% ZZQ;}for(int i =0; i <52; i++)if(cnt[i]){divi(fac[cnt[i]], cnt[i]);for(int j = i +1; j <52; j++)if(cnt[j]){ivid(fac[cnt[j]], cnt[j]);
h[i][j]= h[j][i]= m - cnt[i]- cnt[j]<0?0:
g[m - cnt[i]- cnt[j]];}}while(q--){
x =o(s[read()]); y =o(s[read()]);if(x == y){printf("%d\n",1ll* f[m]* fac[m]% ZZQ * fac[m]% ZZQ);continue;}elseprintf("%d\n",2ll* h[x][y]* inv[cnt[x]]% ZZQ
* inv[cnt[y]]% ZZQ * fac[m]% ZZQ * fac[m]% ZZQ);}return0;}
E
题意
给定一棵
n
n
n 节点的树,
q
q
q 组询问
每次询问三个参数
k
,
m
,
r
k,m,r
k,m,r 以及一个长度为
k
k
k 的数组
a
a
a
询问在以
r
r
r 为根时,把数组
a
a
a 内的节点分成
m
m
m 组,使得每组内任意两点没有祖先后代关系的方案数
1
≤
n
,
q
≤
1
0
5
1\le n,q\le10^5
1≤n,q≤105
每组询问内
1
≤
k
,
r
≤
n
,
1
≤
m
≤
min
(
300
,
k
)
1\le k,r\le n,1\le m\le\min(300,k)
1≤k,r≤n,1≤m≤min(300,k)
所有询问的
k
k
k 之和不超过
1
0
5
10^5
105
算法: DFS 序 + 大讨论 + DP
丧心病狂的换根操作啊!!!!!
先解决两个问题
(1)以
r
r
r 为根时点
u
u
u 的子树大小
(2)以
r
r
r 为根时点
u
u
u 的 DFS 序
下面
d
f
n
[
u
]
dfn[u]
dfn[u] 、
s
i
z
e
[
u
]
size[u]
size[u] 、
d
e
p
[
u
]
dep[u]
dep[u] 分别为以
1
1
1 为根时
u
u
u 的 DFS 序、子树大小、深度
问题(1)较好处理,有三种情况
①
u
=
r
u=r
u=r :
u
u
u 的子树大小为
n
n
n
② 在以
1
1
1 为根的树上
u
u
u 不是
r
r
r 的祖先:
u
u
u 的子树大小为
s
i
z
e
[
u
]
size[u]
size[u]
③否则
u
u
u 的子树大小为
n
−
s
i
z
e
[
x
]
n-size[x]
n−size[x] ,其中
x
x
x 为在树上从
u
u
u 走到
r
r
r 的过程中经过的第二个点(由于在以
1
1
1 为根的树上
u
u
u 是
r
r
r 的祖先,可以用倍增求出
x
x
x )
问题(2)就是真正的大讨论了
为了方便讨论,我们假设对于任意
1
1
1 到
r
r
r 的路径上的点
x
x
x (
1
1
1 除外),把
f
a
[
x
]
fa[x]
fa[x] 设定为以
r
r
r 为根时
x
x
x 的第一个子节点(
f
a
[
x
]
fa[x]
fa[x] 为以
1
1
1 为根时
x
x
x 的父亲节点)
①
u
=
r
u=r
u=r :
u
u
u 的 DFS 序为
1
1
1
②以
1
1
1 为根时
u
u
u 在
r
r
r 的子树(不包括
r
r
r )内:
u
u
u 的 DFS 序为
n
−
s
i
z
e
[
r
]
+
d
f
n
[
u
]
−
d
f
n
[
r
]
+
1
n-size[r]+dfn[u]-dfn[r]+1
n−size[r]+dfn[u]−dfn[r]+1
③以
1
1
1 为根时
u
u
u 是
r
r
r 的祖先(不包括
r
r
r ):
u
u
u 的 DFS 序为
d
e
p
[
r
]
−
d
e
p
[
u
]
+
1
dep[r]-dep[u]+1
dep[r]−dep[u]+1
④否则设
x
x
x 为
u
u
u 与
r
r
r 的 LCA ,
y
y
y 为在树上从
x
x
x 走到
r
r
r 的过程中经过的第二个点(同样也可用倍增求),这时
u
u
u 的 DFS 序为
d
e
p
[
r
]
−
d
e
p
[
x
]
+
n
−
s
i
z
e
[
x
]
+
w
h
i
c
h
(
x
,
y
,
u
)
dep[r]-dep[x]+n-size[x]+which(x,y,u)
dep[r]−dep[x]+n−size[x]+which(x,y,u)
其中
w
h
i
c
h
(
x
,
y
,
u
)
which(x,y,u)
which(x,y,u) 表示在集合
[
d
f
n
[
x
]
,
d
f
n
[
x
]
+
s
z
e
[
x
]
)
−
[
d
f
n
[
y
]
,
d
f
n
[
y
]
+
s
z
e
[
y
]
)
[dfn[x],dfn[x]+sze[x])-[dfn[y],dfn[y]+sze[y])
[dfn[x],dfn[x]+sze[x])−[dfn[y],dfn[y]+sze[y]) 中,
d
f
n
[
u
]
dfn[u]
dfn[u] 的排名
分
d
f
n
[
u
]
<
d
f
n
[
y
]
dfn[u]<dfn[y]
dfn[u]<dfn[y] 和
d
f
n
[
u
]
≥
d
f
n
[
y
]
+
s
z
e
[
y
]
dfn[u]\ge dfn[y]+sze[y]
dfn[u]≥dfn[y]+sze[y] 两类进行讨论
大讨论结束之后我们就可以考虑通过 DP 算方案数辣
虚树 + 树形背包 DP 的做法很容易想到,但是不好写 + 常数大
我们 DP 的策略:先把所有点按照以
r
r
r 为根的 DFS 序进行排序
然后
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示所有 DFS 序前
i
i
i 小的关键点分成
j
j
j 个合法集合的方案数
我们知道, DFS 序有一个性质:对于任意点
u
u
u ,
u
u
u 的所有严格祖先的 DFS 序严格小于
u
u
u 的 DFS 序
初始状态
f
[
0
]
[
0
]
=
1
f[0][0]=1
f[0][0]=1
有两种转移
(1)
i
+
1
i+1
i+1 独自分到一个集合
f
[
i
+
1
]
[
j
+
1
]
+
=
f
[
i
]
[
j
]
f[i+1][j+1]+=f[i][j]
f[i+1][j+1]+=f[i][j]
(2)
i
+
1
i+1
i+1 加入前面已经生成过的集合内
根据 DFS 序的性质,与
i
+
1
i+1
i+1 互斥的所有点(严格祖先)一定全部出现在
[
1
,
i
]
[1,i]
[1,i] 中,而
i
+
1
i+1
i+1 的所有严格祖先又必然地两两存在祖先关系,故
i
+
1
i+1
i+1 的所有严格祖先中,任意两个点都不可能在一个集合内。所以当前的
j
j
j 个集合中,必然会有恰好
c
n
t
[
i
+
1
]
cnt[i+1]
cnt[i+1] 个集合由于包含了
i
+
1
i+1
i+1 的祖先而不能和
i
+
1
i+1
i+1 并到一个集合内,还剩下
j
−
c
n
t
[
i
+
1
]
j-cnt[i+1]
j−cnt[i+1] 个集合可以让点
i
+
1
i+1
i+1 加入(
c
n
t
[
i
]
cnt[i]
cnt[i] 表示以
r
r
r 为根的树上, DFS 序排名为
i
i
i 的关键点的所有严格祖先中有多少个关键点,可以使用树上差分 + 树状数组等手段求出),于是这个转移也出来了
f
[
i
+
1
]
[
j
]
+
=
f
[
i
]
[
j
]
×
(
j
−
c
n
t
[
i
+
1
]
)
f[i+1][j]+=f[i][j]\times(j-cnt[i+1])
f[i+1][j]+=f[i][j]×(j−cnt[i+1])
复杂度
O
(
n
log
n
+
(
∑
k
)
(
log
n
+
m
)
)
O(n\log n+(\sum k)(\log n+m))
O(nlogn+(∑k)(logn+m))
代码
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}template<classT>inlinevoidSwap(T &a, T &b){T t = a; a = b; b = t;}constint N =1e5+5, M = N <<1, LogN =20, E =305, ZZQ =1e9+7;int n, q, ecnt, nxt[M], adj[N], go[M], T, dfn[N], sze[N], fa[N][LogN], dep[N],
xdfn[N], xsze[N], k, m, r, a[N], A[N], cnt[N], f[N][E];voidchange(int x,int v){for(; x <= n; x += x &-x)
A[x]+= v;}intask(int x){int res =0;for(; x; x -= x &-x)
res += A[x];return res;}voidchange(int l,int r,int v){change(l, v);change(r +1,-v);}voidadd_edge(int u,int v){
nxt[++ecnt]= adj[u]; adj[u]= ecnt; go[ecnt]= v;
nxt[++ecnt]= adj[v]; adj[v]= ecnt; go[ecnt]= u;}voiddfs(int u,int fu){
dfn[u]=++T; sze[u]=1;
dep[u]= dep[fa[u][0]= fu]+1;for(int i =0; i <16; i++)
fa[u][i +1]= fa[fa[u][i]][i];for(int e = adj[u], v; e; e = nxt[e]){if((v = go[e])== fu)continue;dfs(v, u);
sze[u]+= sze[v];}}intlca(int u,int v){if(dep[u]< dep[v])Swap(u, v);for(int i =16; i >=0; i--){if(dep[fa[u][i]]>= dep[v]) u = fa[u][i];if(u == v)return u;}for(int i =16; i >=0; i--)if(fa[u][i]!= fa[v][i])
u = fa[u][i], v = fa[v][i];return fa[u][0];}intjm(int u,int dis){for(int i =16; i >=0; i--)if((dis >> i)&1) u = fa[u][i];return u;}intwhich(int l,int r,int inl,int inr,int id){if(id < inl)return id - l;return id - l -(inr - inl +1);}intget_sze(int u,int rt){if(dfn[rt]< dfn[u]|| dfn[rt]>= dfn[u]+ sze[u])return sze[u];if(u == rt)return n;return n - sze[jm(rt, dep[rt]- dep[u]-1)];}intget_dfn(int u,int rt){if(u == rt)return1;if(dfn[rt]<= dfn[u]&& dfn[u]< dfn[rt]+ sze[rt])return n - sze[rt]+ dfn[u]- dfn[rt]+1;int x =lca(u, rt), y;if(u == x)return dep[rt]- dep[u]+1;int delta = dep[rt]- dep[x]+1+ n - sze[x];
y =jm(rt, dep[rt]- dep[x]-1);return delta +which(dfn[x], dfn[x]+ sze[x]-1,
dfn[y], dfn[y]+ sze[y]-1, dfn[u]);}inlineboolcomp(int a,int b){return xdfn[a]< xdfn[b];}intmain(){int x, y;
n =read(); q =read();for(int i =1; i < n; i++) x =read(), y =read(),add_edge(x, y);dfs(1,0);
f[0][0]=1;while(q--){
k =read(); m =read(); r =read();for(int i =1; i <= k; i++){
a[i]=read();
xdfn[a[i]]=get_dfn(a[i], r);
xsze[a[i]]=get_sze(a[i], r);change(xdfn[a[i]]+1, xdfn[a[i]]+ xsze[a[i]]-1,1);for(int j =0; j <= m; j++) f[i][j]=0;}
std::sort(a +1, a + k +1, comp);for(int i =1; i <= k; i++) cnt[i]=ask(xdfn[a[i]]);for(int i =1; i <= k; i++)change(xdfn[a[i]]+1, xdfn[a[i]]+ xsze[a[i]]-1,-1);for(int i =0; i < k; i++)for(int j =0; j <= m; j++){if(!f[i][j])continue;if(j < m) f[i +1][j +1]=(f[i +1][j +1]+ f[i][j])% ZZQ;
f[i +1][j]=(1ll* f[i][j]*(j - cnt[i +1])+ f[i +1][j])% ZZQ;}int ans =0;for(int i =0; i <= m; i++) ans =(ans + f[k][i])% ZZQ;printf("%d\n", ans);}return0;}