常识:
n
m
=
∑
i
=
1
m
S
m
,
i
(
n
i
)
i
!
=
∑
i
=
1
m
S
m
,
i
n
i
‾
n^m=\sum_{i=1}^mS_{m,i}\binom{n}{i}i!=\sum_{i=1}^mS_{m,i} n^{\underline i}
nm=i=1∑mSm,i(in)i!=i=1∑mSm,ini
组合意义就是有一个长为 m 的序列,每个点刷 n 种颜色,枚举最后有几中颜色
把
(
−
n
)
(-n)
(−n) 代上去
(
−
n
)
m
=
∑
i
=
1
m
S
m
,
i
(
−
n
)
i
‾
=
∑
i
=
1
m
S
m
,
i
(
−
1
)
i
n
i
‾
(-n)^m=\sum_{i=1}^mS_{m,i} (-n)^{\underline i}=\sum_{i=1}^mS_{m,i} (-1)^in^{\overline i}
(−n)m=i=1∑mSm,i(−n)i=i=1∑mSm,i(−1)ini
n
m
=
∑
i
=
1
m
S
m
,
i
(
−
1
)
m
−
i
n
i
‾
n^m=\sum_{i=1}^mS_{m,i} (-1)^{m-i}n^{\overline i}
nm=i=1∑mSm,i(−1)m−ini
n
m
‾
=
∑
i
=
1
m
s
m
,
i
n
i
n^{\overline m}=\sum_{i=1}^m s_{m,i} n^i
nm=i=1∑msm,ini
用归纳法证明
n
m
‾
=
n
m
−
1
‾
∗
(
n
+
m
−
1
)
=
∑
i
=
1
m
−
1
s
m
−
1
,
i
n
i
(
n
+
m
−
1
)
n^{\overline m}=n^{\overline {m-1}}*(n+m-1)=\sum_{i=1}^{m-1}s_{m-1,i}n^i(n+m-1)
nm=nm−1∗(n+m−1)=i=1∑m−1sm−1,ini(n+m−1)
第
k
k
k 项的系数是
s
m
−
1
,
i
−
1
+
(
m
−
1
)
s
m
−
1
,
i
=
s
m
,
i
s_{m-1,i-1}+(m-1)s_{m-1,i}=s_{m,i}
sm−1,i−1+(m−1)sm−1,i=sm,i
同样代
(
−
n
)
(-n)
(−n)
(
−
n
)
m
‾
=
∑
i
=
1
m
s
m
,
i
(
−
n
)
i
(-n)^{\overline m}=\sum_{i=1}^m s_{m,i} (-n)^i
(−n)m=i=1∑msm,i(−n)i
n
m
‾
=
∑
i
=
1
m
s
m
,
i
(
−
1
)
m
−
i
n
i
n^{\underline m}=\sum_{i=1}^m s_{m,i} (-1)^{m-i} n^i
nm=i=1∑msm,i(−1)m−ini
TCO14 CountTables
用
f
m
f_m
fm 表示 m 列的答案,先强制行不相等,再减去列相等的情况
f
m
=
n
!
∗
(
c
m
n
)
−
∑
i
=
1
m
−
1
f
i
∗
S
m
,
i
f_m=n!*\binom{c^m}{n}-\sum_{i=1}^{m-1}f_i*S_{m,i}
fm=n!∗(ncm)−∑i=1m−1fi∗Sm,i
题解:[HDU4625] JZPTREE
一道比较妙的斯特林转下降幂的题
题解:HackerRank costly graphs
同样用斯特林转下降幂
CyclesNumber
求所有长度为 n 的置换的循环个数的 m 次方和。
n
≤
1
e
5
,
m
,
T
≤
300
n\le 1e5,m,T\le 300
n≤1e5,m,T≤300
令
x
i
x_i
xi 表示循环 i 是否存在,那么一个置换求的就是
(
∑
x
i
)
m
(\sum x_i)^m
(∑xi)m
同样用斯特林化简
(
∑
x
i
)
m
=
∑
k
=
0
m
S
m
,
k
k
!
(
∑
x
i
k
)
(\sum x_i)^m=\sum_{k=0}^mS_{m,k}k!\binom{\sum x_i}{k}
(∑xi)m=∑k=0mSm,kk!(k∑xi)
也就是说某 k 个循环同时存在对答案的贡献为
S
m
,
k
∗
k
!
S_{m,k}*k!
Sm,k∗k!
考虑如何求某 k 个循环同时存在的方案数,我们可以选出 k 个循环然后将剩下的破开与 n+1 再形成一个循环,方案数为
s
n
+
1
,
k
+
1
s_{n+1,k+1}
sn+1,k+1
所以
a
n
s
=
∑
k
=
0
m
s
n
+
1
,
k
+
1
∗
S
m
,
k
∗
k
!
ans=\sum_{k=0}^ms_{n+1,k+1}*S_{m,k}*k!
ans=∑k=0msn+1,k+1∗Sm,k∗k!
当然
a
n
s
=
∑
k
=
1
n
s
n
,
k
∗
k
m
ans=\sum_{k=1}^ns_{n,k}*k^m
ans=∑k=1nsn,k∗km用斯特林化简
k
m
k^m
km 也是同样的结果
CodeChef-SUMCUBE
令
x
i
,
S
x_{i,S}
xi,S 表示 i 这条边是否在 S 中
那么求的就是
∑
S
(
∑
x
i
,
S
)
m
\sum_S (\sum x_{i,S})^m
∑S(∑xi,S)m
考虑 k 条边,它们同时存在,对答案的贡献是
S
m
,
k
∗
k
!
S_{m,k}*k!
Sm,k∗k!
同时存在当且仅当 S 包涵了它们端点的并集,并集大小为
w
w
w,方案数就为
2
n
−
w
2^{n-w}
2n−w
关于
S
m
,
k
∗
k
!
S_{m,k}*k!
Sm,k∗k! 算贡献的理解:
首先需要知道
n
m
=
∑
k
=
0
S
m
,
k
k
!
(
n
k
)
n^m=\sum_{k=0}S_{m,k}k!\binom{n}{k}
nm=∑k=0Sm,kk!(kn)
在这道题中,暴力做法是先枚举点集
S
S
S,再知道之中的边数
x
x
x
对答案的贡献是
∑
k
=
0
m
S
m
,
k
k
!
(
x
k
)
\sum_{k=0}^m S_{m,k}k!\binom{x}{k}
∑k=0mSm,kk!(kx)
也就是说,从 x 中选 k 个出来算一下贡献
反过来想,就是在集合中任选 k 个,加上
S
m
,
k
∗
k
!
S_{m,k}*k!
Sm,k∗k! 的贡献,而正好会算
(
x
k
)
\binom{x}{k}
(kx) 次
也就是说答案可以表示为
a
n
s
=
∑
S
∑
n
o
d
e
∈
S
S
m
,
k
∗
k
!
ans=\sum_S \sum_{node\in S}S_{m,k}*k!
ans=∑S∑node∈SSm,k∗k!,
n
o
d
e
node
node 是 k 条边的并集
于是可以枚举 k 条边 ,每 k 条同时存在的边会被算
2
n
−
w
2^{n-w}
2n−w 次
显然不能枚举 k 条边是什么
我们分
k
=
1
,
2
,
3
k = 1,2,3
k=1,2,3,分别统计两端并集大小为
2
,
3
,
4
,
5
,
6
2, 3, 4, 5, 6
2,3,4,5,6 的方案数
中间需要用到 3 元环计数:
如果
d
e
g
x
>
d
e
g
y
deg_x>deg_y
degx>degy 或
d
e
g
x
=
d
e
g
y
,
x
>
y
deg_x=deg_y,x>y
degx=degy,x>y 那么 x 向 y 连一条边
然后枚举 x,将它的出点标记成 x,然后取它的一个出点,对于出点的出点看一下标记是不是 x
由于我们的建边方式,每个三元环只会被统计一次
复杂度:每条边对复杂度的贡献是
d
e
g
y
deg_y
degy
如果
d
e
g
y
<
m
deg_y < \sqrt m
degy<m 那么复杂度最多
O
(
m
m
)
O(m\sqrt m)
O(mm)
如果
d
e
g
y
>
m
deg_y>\sqrt m
degy>m, 那么
d
e
g
x
>
d
e
g
y
>
m
deg_x > deg_y>\sqrt m
degx>degy>m ,x 不会超过
m
\sqrt m
m 个,也就是说每一个
d
e
g
v
deg_v
degv 的贡献最大就是
d
e
g
v
∗
m
deg_v*\sqrt m
degv∗m,而
∑
d
e
g
v
=
m
\sum deg_v = m
∑degv=m ,所以复杂度是
O
(
m
m
)
O(m \sqrt m)
O(mm)
#include<bits/stdc++.h>
#define N 100050
using namespace std;
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = getchar();
return cnt * f;
}
typedef long long ll;
const int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
int power(int a, int b, int ans = 1){
if(!ans) return 0;
for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);}
return ans;
}
const int inv2 = (Mod + 1) / 2, inv3 = (Mod + 1) / 3, inv6 = mul(inv2, inv3);
int c2(int n){ return mul(mul(n, n-1), inv2);}
int c3(int n){ return mul(mul(n, n-1), mul(n-2, inv6));}
int s[4][4] = {{1}, {0, 1}, {0, 1, 1}, {0, 1, 3, 1}};
int *S;
int n, m, k, ans, d[N];
vector<int> E[N], G[N];
int calc2(){
int t1 = 0, t2 = c2(m);
for(int i = 1; i <= n; i++) t1 = add(t1, c2(d[i]));
t2 = add(t2, Mod - t1);
return mul(S[2] + S[2], add(power(2, n - 3, t1), power(2, n - 4, t2)));
}
bool cmp(int u, int v){ return d[u] > d[v] || (d[u] == d[v] && u > v);}
int vis[N], idx;
int ring(){
int cnt = 0;
for(int u = 1; u <= n; u++){
for(int i = 0; i < E[u].size(); i++){
int v = E[u][i]; if(cmp(u, v)) G[u].push_back(v);
}
}
for(int u = 1; u <= n; u++){
++idx;
for(int i = 0; i < G[u].size(); i++) vis[G[u][i]] = idx;
for(int i = 0; i < G[u].size(); i++){
int v = G[u][i];
for(int j = 0; j < G[v].size(); j++) if(vis[G[v][j]] == idx) ++cnt;
}
} return cnt;
}
int calc3(){
int t1 = ring(), t2 = 0, t3 = 0, t4 = c3(m);
for(int u = 1; u <= n; u++)
for(int i = 0; i < G[u].size(); i++) t2 = add(t2, mul(d[u]-1, d[G[u][i]]-1));
t2 = add(t2, Mod - mul(t1, 3));
t3 = add(t3, Mod - add(t2, t2));
for(int u = 1; u <= n; u++){
t2 = add(t2, c3(d[u]));
t3 = add(t3, mul(m - d[u], c2(d[u])));
}
t3 = add(t3, Mod - mul(t1, 3));
t4 = add(t4, Mod - add(add(t1, t2), t3));
int t = mul(S[3], 6);
return mul(t, add(add(power(2, n-3, t1), power(2, n-4, t2)), add(power(2, n-5, t3), power(2, n-6, t4))));
}
void FSY(){
for(int i = 1; i <= n; i++) G[i].clear(), E[i].clear(), d[i] = 0;
}
void Yolanda(){
n = read(), m = read(), k = read(); S = s[k];
for(int i = 1; i <= m; i++){
int u = read(), v = read();
E[u].push_back(v); E[v].push_back(u);
++d[u]; ++d[v];
}
ans = mul(mul(m, S[1]), power(2, n - 2));
if(k > 1 && m > 1) ans = add(ans, calc2());
if(k > 2 && m > 2) ans = add(ans, calc3());
cout << ans << '\n';
}
int main(){
int T = read();
while(T--) Yolanda(), FSY(); return 0;
}