洛谷 P5488 差分与前缀和
看起来很毒瘤,但是推出贡献系数后就是一个朴素的卷积了。
首先考虑前缀和。考虑 \(j\ (j \le i)\) 的 \(a_j\) 贡献到 \(i\) 的过程,是找到 \(j = p_0 \le p_1 \le \cdots \le p_k = i\) 的方案数。令 \(x_i = p_i - p_{i-1}\),即求 \(x_i \ge 0, \sum\limits_{i=1}^k x_i = i - j\) 的方案数。运用插板法易得方案数为 \(\binom{i-j+k-1}{k-1}\)。
设 \(b_i = \binom{i+k-1}{k-1}\),那么 \(b_0 = 1, b_i = b_{i-1} \times \frac{i+k-1}{i}\)。
那么 \(s_i = \sum\limits_{j=1}^i a_j b_{i-j}\),直接乘法卷积即可。
然后考虑差分。考虑 \(j\ (j \le i)\) 的 \(a_j\) 贡献到 \(i\) 的过程,是找到 \(j = p_0 \le p_1 \le \cdots \le p_k = i\) 且 \(p_i - p_{i-1} \le 1\) 的方案数。令 \(x_i = p_i - p_{i-1}\),即求 \(x_i \in \{0, 1\}, \sum\limits_{i=1}^k x_i = i - j\) 的方案数。相当于从 \(k\) 个变量中选出 \(i - j\) 个赋值为 \(1\),方案数为 \(\binom{k}{i-j}\);同时因为向左一步要取相反数,所以最后的贡献系数为 \((-1)^{i-j} \binom{k}{i-j}\)。
设 \(b_i = (-1)^i \binom{k}{i}\),那么 \(b_0 = 1, b_i = -\frac{(k+1-i)b_{i-1}}{i}\)。
那么 \(s_i = \sum\limits_{j=1}^i a_j b_{i-j}\),直接乘法卷积即可。
因为 \(k\) 很大,直接对 \(mod\) 取模。
code
// Problem: P5488 差分与前缀和
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5488
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 300000;
const ll mod = 1004535809, G = 3;
inline ll qpow(ll b, ll p) {
ll res = 1;
while (p) {
if (p & 1) {
res = res * b % mod;
}
b = b * b % mod;
p >>= 1;
}
return res;
}
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline ll read() {
char c = getchar();
ll x = 0;
for (; !isdigit(c); c = getchar()) ;
for (; isdigit(c); c = getchar()) x = (x * 10 + (c ^ 48)) % mod;
return x;
}
ll n, m, t, a[maxn], b[maxn], r[maxn];
typedef vector<ll> poly;
inline poly NTT(poly a, int op) {
int n = (int)a.size();
for (int i = 0; i < n; ++i) {
if (i < r[i]) {
swap(a[i], a[r[i]]);
}
}
for (int k = 1; k < n; k <<= 1) {
ll wn = qpow(op == 1 ? G : qpow(G, mod - 2), (mod - 1) / (k << 1));
for (int i = 0; i < n; i += (k << 1)) {
ll w = 1;
for (int j = 0; j < k; ++j, w = w * wn % mod) {
ll x = a[i + j], y = w * a[i + j + k] % mod;
a[i + j] = (x + y) % mod;
a[i + j + k] = (x - y + mod) % mod;
}
}
}
return a;
}
inline poly operator * (poly a, poly b) {
a = NTT(a, 1);
b = NTT(b, 1);
int n = (int)a.size();
for (int i = 0; i < n; ++i) {
a[i] = a[i] * b[i] % mod;
}
a = NTT(a, -1);
ll inv = qpow(n, mod - 2);
for (int i = 0; i < n; ++i) {
a[i] = a[i] * inv % mod;
}
return a;
}
void solve() {
n = read();
m = read();
t = read();
for (int i = 1; i <= n; ++i) {
a[i] = read();
}
int k = 0;
while ((1 << k) <= n * 2) {
++k;
}
for (int i = 1; i < (1 << k); ++i) {
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (k - 1));
}
b[0] = 1;
if (!t) {
for (int i = 1; i <= n; ++i) {
b[i] = b[i - 1] * (i + m - 1) % mod * qpow(i, mod - 2) % mod;
}
} else {
for (int i = 1; i <= n; ++i) {
b[i] = (i <= m ? (mod - (m + 1 - i) * qpow(i, mod - 2) % mod * b[i - 1] % mod) % mod : 0);
}
}
poly A, B;
for (int i = 0; i < (1 << k); ++i) {
A.pb(a[i]);
B.pb(b[i]);
}
poly C = A * B;
for (int i = 1; i <= n; ++i) {
printf("%lld ", C[i]);
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}