Description
给定序列 a=(a1,a2,⋯ ,an)a=(a_1,a_2,\cdots,a_n)a=(a1,a2,⋯,an),有 mmm 个操作分三种:
- add(l,r,v)\operatorname{add}(l,r,v)add(l,r,v):对每个 i∈[l,r]i\in [l,r]i∈[l,r] 执行 ai←ai+va_i\gets a_i+vai←ai+v.
- rev(l,r)\operatorname{rev}(l,r)rev(l,r):对每个 i∈[l,r]i\in [l,r]i∈[l,r] 执行 ai←−aia_i\gets -a_iai←−ai.
- query(l,r,c)\operatorname{query}(l,r,c)query(l,r,c):求 (∑b⊆al⋯r,∣b∣=c∏i∈bi) mod 19940417\sum\limits_{b \subseteq a_{l\cdots r},|b|=c} \prod\limits_{i \in b}i) \bmod 19940417b⊆al⋯r,∣b∣=c∑i∈b∏i)mod19940417.
Limitations
1≤n,q≤5×1041 \le n,q \le 5\times 10^41≤n,q≤5×104
1≤l≤r≤n1 \le l \le r \le n1≤l≤r≤n
∣ai∣,∣v∣≤109|a_i|,|v| \le 10^9∣ai∣,∣v∣≤109
1≤c≤min(r−l+1,20)1 \le c \le \min(r-l+1,20)1≤c≤min(r−l+1,20)
6s,250MB6\text{s},250\text{MB}6s,250MB
Solution
虽然 n,qn,qn,q 没到 10510^5105 级别,但暴力维护的复杂度是指数级的.
注意到 ccc 很小,因此复杂度可以带个 ccc,想到 DP
.
记 dpl,r,c=(∑b⊆al⋯r,∣b∣=c∏i∈bi)\textit{dp}_{l,r,c}=(\sum\limits_{b \subseteq a_{l\cdots r},|b|=c} \prod\limits_{i \in b}i)dpl,r,c=(b⊆al⋯r,∣b∣=c∑i∈b∏i).
则可得 dpl,r,c=∑t=0ivl,j,t×dpj+1,r,c−t\textit{dp}_{l,r,c}=\sum\limits_{t=0}^i v_{l,j,t} \times \textit{dp}_{j+1,r,c-t}dpl,r,c=t=0∑ivl,j,t×dpj+1,r,c−t,其中 l<j<rl<j<rl<j<r.
显然 dp\textit{dp}dp 可以在线段树 pushup
时维护。
接下来考虑修改:
- 对于 rev\operatorname{rev}rev:可以发现,只有 ccc 为奇数才要变号。
- 对于 add\operatorname{add}add:考虑把式子展开,发现是一个关于 vvv 的 ccc 次多项式,由二项式定理得 ndpl,r,c=∑i=0k((n−i+kk)×dpl,r,c−k×vk)\textit{ndp}_{l,r,c}=\sum\limits_{i=0}^k(\binom{n-i+k}{k} \times \textit{dp}_{l,r,c-k}\times v^k)ndpl,r,c=i=0∑k((kn−i+k)×dpl,r,c−k×vk).
然后要注意 pushdown
时的顺序。
另外 19970417∉P19970417 \notin \mathbb{P}19970417∈/P,需要用杨辉三角,而不是费马小定理来求组合数.
可得时间复杂度 O(c2nlogn)O(c^2n\log n)O(c2nlogn),空间复杂度 O(cnlogn)O(cn\log n)O(cnlogn).
Code
8.92KB,12.93s,29.68MB (in total, C++ 20 with O2)8.92\text{KB},12.93\text{s},29.68\text{MB}\;\texttt{(in total, C++ 20 with O2)}8.92KB,12.93s,29.68MB(in total, C++ 20 with O2)
线段树,modint
删了.
// Problem: P4247 [清华集训2012] 序列操作
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4247
// Memory Limit: 250 MB
// Time Limit: 6000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
inline int read() {
int x = 0,f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
void write(int x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
return;
}
template <int MOD>
struct modint {};
template<class Info, class Tag>
struct lazy_segment{};
const int D = 20;
using Z = modint<19940417>;
array<Z, D + 1> power;
vector<array<Z, D + 1>> C;
struct Tag {
Z add;
bool rev;
Tag(Z _add = 0, bool _rev = false): add(_add), rev(_rev) {}
void apply(const Tag& t) {
if (t.rev) {
add = -add;
rev ^= 1;
}
if (t.add != 0) {
add += t.add;
}
}
};
struct Info {
int len;
array<Z, D + 1> c;
Info(Z _val = 0, int _len = 1): len(_len) {
c[0] = 1;
c[1] = _val;
}
void apply(const Tag& t) {
int size = min(len, D);
if (t.rev) {
for (int i = 1; i <= size; i += 2) {
c[i] = -c[i];
}
}
if (t.add != 0) {
power[0] = 1;
for (int i = 1; i <= size; i++) {
power[i] = power[i - 1] * t.add;
}
for (int i = size; i; i--) {
for (int j = 0; j < i; j++) {
c[i] += c[j] * power[i - j] * C[len - j][i - j];
}
}
}
}
};
Info operator+(const Info& lhs, const Info& rhs) {
Info res;
res.c.fill(0);
res.len = lhs.len + rhs.len;
int size = min(lhs.len, D), rlen = rhs.len;
for (int i = 0; i <= size; i++) {
for (int j = 0; j + i <= D && j <= rlen; j++) {
res.c[i + j] += lhs.c[i] * rhs.c[j];
}
}
return res;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n = read(), m = read();
C.resize(n + 1);
C[0][0] = 1;
for (int i = 1; i <= n; i++) {
int siz = min(D, i);
C[i][0] = 1;
for (int j = 1; j <= siz; j++) {
C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
}
}
vector<Info> a;
for (int i = 0, v; i < n; i++) {
v = read();
a.emplace_back(Z(v), 1);
}
lazy_segment<Info, Tag> tree(a);
for (int i = 0; i < m; i++) {
char op = getchar();
while (op != 'I' && op != 'R' && op != 'Q') {
op = getchar();
}
int l = read(), r = read(), v;
l--, r--;
if (op == 'I') {
v = read();
tree.apply(l, r, Tag(v, false));
}
if (op == 'R') {
tree.apply(l, r, Tag(0, true));
}
if (op == 'Q') {
v = read();
write(tree.query(l, r).c[v].val);
printf("\n");
}
}
return 0;
}