洛谷 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;
}

posted @ 2023-05-11 19:02  zltzlt  阅读(66)  评论(0编辑  收藏  举报