HackerRank costly graphs [二类斯特林]

本文探讨了使用斯特林数简化多项式的幂运算,并通过高效的多项式乘法算法,如快速傅里叶变换(FFT)的变种——离散傅里叶变换(DFT),来加速计算过程。代码示例展示了如何在C++中实现这一算法,包括预处理、逆变换和多项式乘法等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

因为每个点是独立的,我们可以枚举每个点的度数

ans=n*2^{\binom{n-1}{2}}\sum_{i=0}^ {n-1} \binom{n-1}{i}i^m

考虑用斯特林数化简 i ^ m

 \sum_{i=0}^ {n-1} \binom{n-1}{i}i^m=\sum_{i=0}^{n-1} \binom{n-1}{i} \sum_{j=0}^{m}S_{m, j} \binom{i}{j}j!= \sum_{j= 0}^m S_{m,j} \sum_{i=0}^{n-1} \binom{n-1}{i} \binom{i}{j}* j! = \sum_{j= 0}^m S_{m,j} \sum_{i=0}^{n-1} \binom{n-1}{j} \binom{n-1-j}{i-j}* j! =\sum_{j= 0}^m S_{m,j} \binom{n-1}{j} j!\sum_{i=0}^{n-1} \binom{n-1-j}{i-j}=\sum_{j= 0}^m S_{m,j} \binom{n-1}{j} j! 2^{n-1-j}


#include<bits/stdc++.h>
#define N 500050
using namespace std;
typedef long long ll;
#define poly vector<ll>
#define C 18
const int Mod = 1005060097, G = 5, inv2 = (Mod+1)/2;
ll add(ll a, ll b){ return (a + b) % Mod;}
ll mul(ll a, ll b){ return a * b % Mod;}
ll power(ll a, ll b){ ll ans = 1;
	for(;b;b>>=1){ if(b&1) ans = mul(ans, a); a = mul(a, a);}
	return ans; 
}
int T, n, m;
int up, bit, rev[N]; ll fac[N], ifac[N];
poly w[C + 1];
void prework(){
	for(int i = 1; i <= C; i++) w[i].resize(1 << (i-1));
	ll wn = power(G, (Mod - 1) / (1 << C));
	w[C][0] = 1;
	for(int i = 1; i < (1 << (C-1)); i++) w[C][i] = mul(w[C][i-1], wn);
	for(int i = C-1; i; i--)
		for(int j = 0; j < (1 << (i-1)); j++)
			w[i][j] = w[i+1][j << 1];
}
void Init(int len){ up = 1, bit = 0;
	while(up < len) up <<= 1, bit++;
	for(int i = 0; i < up; i++) rev[i] = (rev[i>>1]>>1) | ((i&1) << (bit-1));
}
void NTT(poly &a, int flag){
	for(int i = 0; i < up; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
	for(int i = 1, l = 1; i < up; i <<= 1, l++) 
		for(int j = 0; j < up; j += (i<<1))
			for(int k = 0; k < i; k++){
				ll x = a[k + j], y = mul(w[l][k], a[k + j + i]);
				a[k + j] = add(x, y); a[k + j + i] = add(x, Mod - y);
			}
	if(flag == -1){
		reverse(a.begin() + 1, a.begin() + up); ll inv = power(up, Mod - 2);
		for(int i = 0; i < up; i++) a[i] = mul(a[i], inv);
	}
}
poly operator * (poly a, poly b){
	int len = a.size() + b.size() - 1;
	Init(len); a.resize(up); b.resize(up);
	NTT(a, 1); NTT(b, 1); for(int i = 0; i < up; i++) a[i] = mul(a[i], b[i]);
	NTT(a, -1); a.resize(len); return a;
}
poly a, b; 
int main(){ 
	scanf("%d", &T); prework();
	fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
	for(int i = 2; i <= N-50; i++) fac[i] = mul(fac[i-1], i);
	ifac[N-50] = power(fac[N-50], Mod-2);
	for(int i = N-51; i>=2; i--) ifac[i] = mul(ifac[i+1], i+1); 
	while(T--){
		scanf("%d%d", &n, &m);
		a.resize(m + 1); b.resize(m + 1);
		for(int i = 0; i <= m; i++) a[i] = mul(power(i, m), ifac[i]);
		for(int i = 0; i <= m; i++) b[i] = mul((i&1) ? Mod-1 : 1, ifac[i]);
		a = a * b;
		ll tmp = mul(n, power(2, (1ll * (n-2) * (n-1) / 2) % (Mod-1)));
		ll sum = 0, muln = 1, pow2 = power(2, n-1);
		for(int i = 0; i <= m; i++){
			if(i == n) break;
			sum = add(sum, mul(a[i], mul(muln, pow2)));
			muln = mul(muln, (n - i - 1));
			pow2 = mul(pow2, inv2);
		} cout << mul(sum, tmp) << "\n";
	} return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FSYo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值