【TJOI/HEOI2016】【BZOJ4555】【LOJ2058】求和

【题目链接】

【前置技能】

  • NTT
  • 斯特林数

【题解】

  • ∑ i = 0 n ∑ j = 0 i S ( i , j ) ∗ 2 j ∗ j ! \displaystyle \sum _{i=0}^{n} \sum _{j=0}^{i} S(i, j)*2^j*j! i=0nj=0iS(i,j)2jj!
    = ( ∑ i = 1 n ∑ j = 1 n S ( i , j ) ∗ 2 j ∗ j ! ) + 1 = \displaystyle (\sum _{i=1}^{n} \sum _{j=1}^{n} S(i, j)*2^j*j!) + 1 =(i=1nj=1nS(i,j)2jj!)+1 S ( i , j ) S(i, j) S(i,j) i &lt; j i &lt; j i<j时的值为0,所以将 j j j的范围更改并不影响答案。 S ( i , j ) S(i, j) S(i,j) i = 0 i =0 i=0 j = 0 j =0 j=0时,仅在 i = j = 0 i=j=0 i=j=0时的值不为 0 0 0,求和从 1 1 1开始就避免了展开后出现 0 0 0^0 00
    = ( ∑ j = 1 n 2 j ∗ j ! ∑ i = 1 n S ( i , j ) ) + 1 = (\displaystyle \sum _{j=1}^{n} 2^j*j!\sum _{i=1}^{n} S(i, j)) + 1 =(j=1n2jj!i=1nS(i,j))+1 【交换求和顺序】
    = ( ∑ j = 1 n 2 j ∗ j ! ∑ i = 1 n 1 j ! ∑ t = 0 j ( − 1 ) t ∗ C j t ∗ ( j − t ) i ) + 1 = (\displaystyle \sum _{j=1}^{n} 2^j*j!\sum _{i=1}^{n} \frac {1} {j!} \sum _{t = 0}^{j} (-1)^t * C_j^t * (j - t)^i ) + 1 =(j=1n2jj!i=1nj!1t=0j(1)tCjt(jt)i)+1 【展开 S ( i , j ) S(i, j) S(i,j)
    = ( ∑ j = 1 n 2 j ∗ j ! ∑ i = 1 n ∑ t = 0 j ( − 1 ) t ∗ 1 t ! ∗ ( j − t ) ! ∗ ( j − t ) i ) + 1 = (\displaystyle \sum _{j=1}^{n} 2^j*j!\sum _{i=1}^{n} \sum _{t = 0}^{j} (-1)^t * \frac{1}{t!*(j -t)!} * (j - t)^i ) + 1 =(j=1n2jj!i=1nt=0j(1)tt!(jt)!1(jt)i)+1 【展开 C j t C_j^t Cjt并化简】
    = ( ∑ j = 1 n 2 j ∗ j ! ∑ t = 0 j ( − 1 ) t t ! ∗ ∑ i = 1 n ( j − t ) i ( j − t ) ! ) + 1 = (\displaystyle \sum _{j=1}^{n} 2^j*j! \sum _{t = 0}^{j} \frac{(-1)^t}{t!} * \frac {\sum _{i=1}^{n}(j - t)^i}{(j -t)!} ) + 1 =(j=1n2jj!t=0jt!(1)t(jt)!i=1n(jt)i)+1 【卷积满足结合律,交换求和顺序】
  • 发现出现了卷积的形式,令 A i = ( − 1 ) i i ! A_i=\frac{(-1)^i}{i!} Ai=i!(1)i B i = ∑ j = 1 n i j i ! B_i = \frac {\sum _{j=1}^{n}i^j}{i!} Bi=i!j=1nij,则 A N S = ( ∑ i = 1 n 2 i ∗ i ! ∗ [ x i ] ( A ∗ B ) ) + 1 ANS = (\displaystyle \sum _{i=1}^{n} 2^i*i! *[x^i](A*B)) + 1 ANS=(i=1n2ii![xi](AB))+1
  • 时间复杂度 O ( N l o g N ) O(NlogN) O(NlogN)

【代码】

#include<bits/stdc++.h>
#define	INF	0x3f3f3f3f
#define	LL	long long
#define	P	998244353
#define	G	3
#define	MAXN	262145
using namespace std;
int n, fac[MAXN], inv[MAXN], ans, Pow[MAXN]; 
int N, LOG, rev[MAXN], A[MAXN], B[MAXN];

template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
	x = 0; int f = 1; char ch = getchar();
	while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
	while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
	x *= f;
}

void update(int &x, int y){x += y; if (x >= P) x -= P;}
int inc(int x, int y){return (x + y >= P) ? (x + y - P) : (x + y);}
int dec(int x, int y){return (x - y < 0) ? (x - y + P) : (x - y);}
int mul(int x, int y){return 1ll * x * y % P;}

int qpow(int a, int b){
	int ret = 1;
	while (b) {
		if (b & 1) ret = mul(ret, a);
		a = mul(a, a);
		b >>= 1;
	}
	return ret;
}

void init(int n){
	N = 1, LOG = 0;
	while (N <= n * 2) N <<= 1, ++LOG;
	for (int i = 0; i < N; ++i)
		rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (LOG - 1));
}

void ntt(int *a, int opt){
	for (int i = 0; i < N; ++i)
		if (rev[i] < i) swap(a[i], a[rev[i]]);
	for (int len = 2; len <= N; len <<= 1){
		int delta;
		if (opt == 1) delta = qpow(G, (P - 1) / len);
		else delta = qpow(G, P - 1 - (P - 1) / len);
		for (int i = 0; i < N; i += len){
			int cur = 1;
			for (int j = i, t = i + len / 2; t < i + len; ++j, ++t){
				int tmp = a[j], tnp = mul(cur, a[t]);
				a[j] = inc(tmp, tnp), a[t] = dec(tmp, tnp);
				cur = mul(cur, delta);
			}
		}
	}
	if (opt == -1) {
		int INV = qpow(N, P - 2);
		for (int i = 0; i < N; ++i)
			a[i] = mul(a[i], INV);
	}
}

void init(){
	fac[0] = inv[0] = 1;
	for (int i = 1; i <= n; ++i)
		fac[i] = mul(fac[i - 1], i);
	inv[n] = qpow(fac[n], P - 2);
	for (int i = n - 1; i >= 1; --i)
		inv[i] = mul(inv[i + 1], i + 1);
	Pow[0] = 1;
	for (int i = 1; i <= n; ++i)
		Pow[i] = mul(Pow[i - 1], 2);
}

int main(){
	read(n);
	init(n);
	init();
	for (int i = 0; i <= n; ++i){
		if (i & 1) A[i] = P - inv[i];
		else A[i] = inv[i];
		B[i] = inv[i];
		if (!i) {B[i] = 0; continue;}
		if (i == 1) {B[i] = n; continue;}
		B[i] = mul(B[i], mul(inv[i - 1], fac[i - 2]));
		B[i] = mul(B[i], dec(qpow(i, n + 1), i));
	}
	ntt(A, 1), ntt(B, 1);
	for (int i = 0; i < N; ++i)
		A[i] = mul(A[i], B[i]);
	ntt(A, -1);
	for (int i = 0; i <= n; ++i){
		int tmp = mul(Pow[i], fac[i]);
		tmp = mul(tmp, A[i]);
		update(ans, tmp);
	}
	update(ans, 1);
	printf("%d\n", ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值