暑假集训 ---- 斯特林数

常识:
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=1mSm,i(in)i!=i=1mSm,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=1mSm,i(n)i=i=1mSm,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=1mSm,i(1)mini


n m ‾ = ∑ i = 1 m s m , i n i n^{\overline m}=\sum_{i=1}^m s_{m,i} n^i nm=i=1msm,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=nm1(n+m1)=i=1m1sm1,ini(n+m1)
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} sm1,i1+(m1)sm1,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=1msm,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=1msm,i(1)mini


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=1m1fiSm,i


题解:[HDU4625] JZPTREE
一道比较妙的斯特林转下降幂的题


题解:HackerRank costly graphs
同样用斯特林转下降幂


CyclesNumber
求所有长度为 n 的置换的循环个数的 m 次方和。
n ≤ 1 e 5 , m , T ≤ 300 n\le 1e5,m,T\le 300 n1e5,m,T300
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!(kxi)
也就是说某 k 个循环同时存在对答案的贡献为 S m , k ∗ k ! S_{m,k}*k! Sm,kk!
考虑如何求某 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+1Sm,kk
当然 a n s = ∑ k = 1 n s n , k ∗ k m ans=\sum_{k=1}^ns_{n,k}*k^m ans=k=1nsn,kkm用斯特林化简 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,kk!
同时存在当且仅当 S 包涵了它们端点的并集,并集大小为 w w w,方案数就为 2 n − w 2^{n-w} 2nw
关于 S m , k ∗ k ! S_{m,k}*k! Sm,kk! 算贡献的理解:
首先需要知道 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,kk! 的贡献,而正好会算 ( 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=SnodeSSm,kk! n o d e node node 是 k 条边的并集
于是可以枚举 k 条边 ,每 k 条同时存在的边会被算 2 n − w 2^{n-w} 2nw
显然不能枚举 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 degvm ,而 ∑ 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值