# [TJOI2016/HEOI2016] 求和 线性解法

16 篇文章 0 订阅

∑ i = 0 n ∑ j = 0 i \{ i j \} ⋅ 2 j ⋅ j ! \sum_{i=0}^n \sum_{j=0}^i {i \brace j} \cdot 2^j \cdot j!

a n s = ∑ i = 0 n ∑ j = 0 i j ! ⋅ \{ i j \} ⋅ 2 j = ∑ i = 0 n ∑ j = 0 n j ! ⋅ \{ i j \} ⋅ 2 j = ∑ i = 0 n ∑ j = 0 n 2 j ∑ k = 0 j ( − 1 ) j − k ( j k ) k i = ∑ j = 0 n ∑ k = 0 j ( − 1 ) j − k ( j k ) 2 j [ n + 1 ] k [ n ] q = ∑ i = 0 n − 1 q i is q-analog \begin{aligned} ans &amp; = \sum_{i=0}^n \sum_{j=0}^i j!\cdot {i \brace j} \cdot 2^j \\ &amp; = \sum_{i=0}^n \sum_{j=0}^n j!\cdot {i \brace j} \cdot 2^j \\ &amp;= \sum_{i=0}^n\sum_{j=0}^n 2^j \sum_{k=0}^j (-1)^{j-k} \binom jk k^i \\ &amp;= \sum_{j=0}^n \sum_{k=0}^j (-1)^{j-k}\binom jk 2^j [n+1]_k \quad [n]_q = \sum_{i=0}^{n-1}q^i \quad\text{is \href{https://en.wikipedia.org/wiki/Q-analog}{q-analog}} \end{aligned}

G ( z − 1 ) = ( 2 z − 2 ) n + 1 − 1 2 z − 3 G(z-1) = \frac{(2z-2)^{n+1} -1}{2z-3}

#include <cstdio>
#include <cstring>

#include <functional>
#include <vector>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;

const int P = 998244353;

void exGcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1;
y = 0;
return;
}
exGcd(b, a % b, y, x);
y -= a / b * x;
}

int inv(int a) {
int x, y;
exGcd(a, P, x, y);
if (x < 0)
x += P;
return x;
}

int mpow(int x, int k) {
int ret = 1;
while (k) {
if (k & 1)
ret = ret * (ll)x % P;
x = x * (ll)x % P;
k >>= 1;
}
return ret;
}

struct Simple {
int n;
vector<int> fac, ifac, inv;

void build(int n) {
this->n = n;
fac.resize(n + 1);
ifac.resize(n + 1);
inv.resize(n + 1);
fac[0] = 1;
for (int x = 1; x <= n; ++x)
fac[x] = fac[x - 1] * (ll)x % P;
inv[1] = 1;
for (int x = 2; x <= n; ++x)
inv[x] = -(P / x) * (ll)inv[P % x] % P + P;
ifac[0] = 1;
for (int x = 1; x <= n; ++x)
ifac[x] = ifac[x - 1] * (ll)inv[x] % P;
}

Simple() {
build(1);
}

void check(int k) {
int nn = n;
if (k > nn) {
while (k > nn)
nn <<= 1;
build(nn);
}
}

int gfac(int k) {
check(k);
return fac[k];
}

int gifac(int k) {
check(k);
return ifac[k];
}

int ginv(int k) {
check(k);
return inv[k];
}

int binom(int n, int m) {
if (m < 0 || m > n)
return 0;
return gfac(n) * (ll)gifac(m) % P * gifac(n - m) % P;
}
} simp;

const int N = 100010, PC = 30010;

int n;
int pc;
int a[N];
bool vis[N];
int p[PC];
int pw[N];

void sieve() {
pw[1] = 1;
for (int x = 2; x <= n; ++x) {
if (!vis[x]) {
p[++pc] = x;
pw[x] = mpow(x, n + 1);
}
for (int i = 1; x * p[i] <= n; ++i) {
vis[x * p[i]] = true;
pw[x * p[i]] = pw[x] * (ll)pw[p[i]] % P;
if (x % p[i] == 0)
break;
}
}
}

int main() {
scanf("%d", &n);
simp.check(n + 1);
for (int i = 0; i <= n; ++i)
a[i] = ((n + 1 - i) & 1) ? (P - simp.binom(n + 1, i)) : simp.binom(n + 1, i);
int pw = mpow(2, n + 1);
for (int i = 0; i <= n; ++i)
a[i] = a[i] * (ll)pw % P;
--a[0];
for (int i = 0; i <= n; ++i)
a[i] = (P - a[i]) % P;
int q = inv(3) * 2 % P;
for (int i = 1; i <= n; ++i)
a[i] = (a[i - 1] * (ll)q + a[i]) % P;
int ans = (a[0] + a[1] * (ll)(n + 1)) % P;
sieve();
for (int i = 2; i <= n; ++i)
ans = (ans + simp.ginv(i - 1) * (::pw[i] - 1LL) % P * a[i]) % P;
if (ans < 0)
ans += P;
ans = ans * (ll)inv(3) % P;
printf("%d\n", ans);

return 0;
}

• 0
点赞
• 2
收藏
觉得还不错? 一键收藏
• 1
评论
07-04 983
11-14 801
03-14 1527
03-29 2134
06-26 646
02-27 719
12-07 2381
04-27 2136
04-25 1887

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