Decription
有一个长度为nn的序列和个操作。
有三种操作
II bb 表示将[a,b][a,b]这一段区间的元素集体增加cc。
aa 表示将[a,b][a,b]区间内所有元素变成相反数。
QQ bb 表示询问[a,b][a,b]这一段区间中选择cc个数相乘的所有方案的和的值。
n,q⩽50000,c⩽20n,q⩽50000,c⩽20
Solution
设DPDP数组fifi表示在当前区间选择ii个数相乘的所有方案的和。每次合并时卷积即可。
对于2操作,将奇数项的值取相反数即可。
而对于11操作,维护的系数,每次添加时计算即可。具体来说
sumi=∑j=1ixj×sumi−j×C(sz−i+j,j)+sumisumi=∑j=1ixj×sumi−j×C(sz−i+j,j)+sumi
#include <bits/stdc++.h>
using namespace std;
typedef long long lint;
const int mod = 19940417;
const int maxn = 50005;
int n, q, C[maxn][21];
inline int gi()
{
char c = getchar(); bool f = 1;
while ((c < '0' || c > '9') && c != '-') c = getchar();
if (c == '-') f = 0, c = getchar();
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return f ? sum : mod - sum;
}
#define lch (s << 1)
#define rch (s << 1 | 1)
#define mid ((l + r) >> 1)
#define Mid (mid + 1)
int f[maxn * 6][21], lazy1[maxn * 6], lazy2[maxn * 6];
inline void merge(int s)
{
for (int i = 1; i <= 20; ++i) {
f[s][i] = 0;
for (int j = 0; j <= i; ++j) {
f[s][i] += (lint)f[lch][j] * f[rch][i - j] % mod;
if (f[s][i] >= mod) f[s][i] -= mod;
}
}
}
int tmp[21];
void increase(int s, int v, int sz)
{
for (int i = min(20, sz); i >= 1; --i) {
tmp[i] = 0;
for (int j = 1, powv = v; j <= i; ++j, powv = (lint)powv * v % mod) {
tmp[i] += (lint)powv * f[s][i - j] % mod * C[sz - i + j][j] % mod;
if (tmp[i] >= mod) tmp[i] -= mod;
}
}
for (int i = min(20, sz); i >= 1; --i) {
f[s][i] += tmp[i];
if (f[s][i] >= mod) f[s][i] -= mod;
}
lazy2[s] += v; if (lazy2[s] >= mod) lazy2[s] -= mod;
}
inline void pushdown(int s, int l, int r)
{
if (lazy1[s]) {
for (int i = 1; i <= 20; i += 2) {
if (f[lch][i]) f[lch][i] = mod - f[lch][i];
if (f[rch][i]) f[rch][i] = mod - f[rch][i];
}
lazy1[lch] ^= 1; if (lazy2[lch]) lazy2[lch] = mod - lazy2[lch];
lazy1[rch] ^= 1; if (lazy2[rch]) lazy2[rch] = mod - lazy2[rch];
lazy1[s] = 0;
}
if (lazy2[s]) {
increase(lch, lazy2[s], mid - l + 1);
increase(rch, lazy2[s], r - Mid + 1);
lazy2[s] = 0;
}
}
void build(int s, int l, int r)
{
f[s][0] = 1;
if (l == r) {
f[s][1] = gi() % mod;
return;
}
build(lch, l, mid);
build(rch, Mid, r);
merge(s);
}
void insert(int s, int l, int r, int x, int y, int v)
{
if (x <= l && r <= y) {
increase(s, v, r - l + 1);
return ;
}
pushdown(s, l, r);
if (x <= mid) insert(lch, l, mid, x, y, v);
if (Mid <= y) insert(rch, Mid, r, x, y, v);
merge(s);
}
void reverse(int s, int l, int r, int x, int y)
{
if (x <= l && r <= y) {
for (int i = 1; i <= 20; i += 2) if (f[s][i]) f[s][i] = mod - f[s][i];
lazy1[s] ^= 1; if(lazy2[s]) lazy2[s] = mod - lazy2[s];
return;
}
pushdown(s, l, r);
if (x <= mid) reverse(lch, l, mid, x, y);
if (Mid <= y) reverse(rch, Mid, r, x, y);
merge(s);
}
int ans[21];
void query(int s, int l, int r, int x, int y)
{
if (x <= l && r <= y) {
for (int i = 1; i <= 20; ++i) {
tmp[i] = 0;
for (int j = 0; j <= i; ++j) {
tmp[i] += (lint)ans[j] * f[s][i - j] % mod;
if (tmp[i] >= mod) tmp[i] -= mod;
}
}
for (int i = 1; i <= 20; ++i) ans[i] = tmp[i];
return ;
}
pushdown(s, l, r);
if (x <= mid) query(lch, l, mid, x, y);
if (Mid <= y) query(rch, Mid, r, x, y);
}
int main()
{
freopen("sequence.in", "r", stdin);
freopen("sequence.out", "w", stdout);
n = gi(); q = gi();
C[0][0] = 1;
for (int i = 1; i <= n; ++i) {
C[i][0] = 1;
for (int j = 1; j <= 20; ++j) {
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
if (C[i][j] >= mod) C[i][j] -= mod;
}
}
build(1, 1, n);
char c;
for (int l, r, i = 1; i <= q; ++i) {
c = getchar();
while (c < 'A' || c > 'Z') c = getchar();
if (c == 'I') l = gi(), r = gi(), insert(1, 1, n, l, r, gi());
else if (c == 'R') l = gi(), r = gi(), reverse(1, 1, n, l, r);
else {
for (int i = 1; i <= 20; ++i) ans[i] = 0;
ans[0] = 1;
l = gi(); r = gi(); query(1, 1, n, l, r);
printf("%d\n", ans[gi()]);
}
}
return 0;
}

本文介绍了一种处理区间操作和复杂查询的算法解决方案。通过动态规划和线段树结合的方法,实现了对序列进行区间加法、区间数值反转及求特定数量元素乘积之和的高效计算。
468

被折叠的 条评论
为什么被折叠?



