题目大意
给出一个序列,有三种操作,区间加,区间取负,询问一个区间内c个数字相乘的所有情况的和。
思路
利用线段树的简单应用,我们不难实现前两个操作。第三个操作需要推推公式。合并两个区间比较简单,计算当前区间取k个时,只需要枚举左边和右边分别取多少个然后乘起来就行了。区间取负把取奇数个数的答案取负,最复杂的是区间加。看代码吧,懒了。。
CODE
#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
#define MO 19940417
using namespace std;
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
#define CNT (r - l + 1)
int C[MAX][25];
inline void U(int &x,int y)
{
x += y;
if(x < 0) x += MO;
if(x >= MO) x -= MO;
}
struct Status{
int ans[25];
int plus;
bool flag;
Status() {
memset(ans,0,sizeof(ans));
plus = flag = 0;
}
Status operator +(const Status &a)const {
Status re;
re.ans[0] = 1;
for(int i = 0; i <= 20; ++i)
for(int j = 0; i + j <= 20; ++j) {
if(i + j == 0) continue;
U(re.ans[i + j],(long long)ans[i] * a.ans[j] % MO);
}
return re;
}
void Plus(int _,int size) {
U(plus,_);
static int temp[25];
for(int i = 1; i <= 20; ++i) {
temp[i] = 0;
int tx = 1;
for(int j = i; ~j; --j) {
U(temp[i],(long long)ans[j] * C[size - j][i - j] % MO * tx % MO);
tx = (long long)tx * _ % MO;
}
}
for(int i = 1; i <= 20; ++i)
ans[i] = temp[i];
}
void Reverse() {
flag ^= 1;
for(int i = 1; i <= 19; i += 2)
ans[i] = (MO - ans[i]) % MO;
plus = (MO - plus) % MO;
}
}tree[MAX << 2];
int src[MAX];
void BuildTree(int l,int r,int pos)
{
tree[pos].ans[0] = 1;
if(l == r) {
tree[pos].ans[1] = src[l];
return;
}
int mid = (l + r) >> 1;
BuildTree(l,mid,LEFT);
BuildTree(mid + 1,r,RIGHT);
tree[pos] = tree[LEFT] + tree[RIGHT];
}
void Pretreatment(int cnt)
{
for(int i = 0; i <= cnt; ++i)
C[i][0] = 1;
for(int i = 1; i <= cnt; ++i)
for(int j = 1; j <= 20; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MO;
}
inline void PushDown(int pos,int cnt)
{
if(tree[pos].flag) {
tree[LEFT].Reverse();
tree[RIGHT].Reverse();
tree[pos].flag = false;
}
if(tree[pos].plus) {
tree[LEFT].Plus(tree[pos].plus,cnt - (cnt >> 1));
tree[RIGHT].Plus(tree[pos].plus,cnt >> 1);
tree[pos].plus = 0;
}
}
void Modify(int l,int r,int x,int y,int c,int pos)
{
if(l == x && y == r) {
tree[pos].Plus(c,CNT);
return;
}
PushDown(pos,CNT);
int mid = (l + r) >> 1;
if(y <= mid) Modify(l,mid,x,y,c,LEFT);
else if(x > mid) Modify(mid + 1,r,x,y,c,RIGHT);
else {
Modify(l,mid,x,mid,c,LEFT);
Modify(mid + 1,r,mid + 1,y,c,RIGHT);
}
tree[pos] = tree[LEFT] + tree[RIGHT];
}
void Reverse(int l,int r,int x,int y,int pos)
{
if(l == x && y == r) {
tree[pos].Reverse();
return;
}
PushDown(pos,CNT);
int mid = (l + r) >> 1;
if(y <= mid) Reverse(l,mid,x,y,LEFT);
else if(x > mid) Reverse(mid + 1,r,x,y,RIGHT);
else {
Reverse(l,mid,x,mid,LEFT);
Reverse(mid + 1,r,mid + 1,y,RIGHT);
}
tree[pos] = tree[LEFT] + tree[RIGHT];
}
Status Ask(int l,int r,int x,int y,int pos)
{
if(l == x && y == r) return tree[pos];
PushDown(pos,CNT);
int mid = (l + r) >> 1;
if(y <= mid) return Ask(l,mid,x,y,LEFT);
if(x > mid) return Ask(mid + 1,r,x,y,RIGHT);
Status left = Ask(l,mid,x,mid,LEFT);
Status right = Ask(mid + 1,r,mid + 1,y,RIGHT);
return left + right;
}
int cnt,asks;
char s[10];
int main()
{
cin >> cnt >> asks;
Pretreatment(cnt);
for(int i = 1; i <= cnt; ++i)
scanf("%d",&src[i]),src[i] = (src[i] % MO + MO) % MO;
BuildTree(1,cnt,1);
for(int x,y,z,i = 1; i <= asks; ++i) {
scanf("%s",s);
if(s[0] == 'I') {
scanf("%d%d%d",&x,&y,&z);
z = (z % MO + MO) % MO;
Modify(1,cnt,x,y,z,1);
}
else if(s[0] == 'R') {
scanf("%d%d",&x,&y);
Reverse(1,cnt,x,y,1);
}
else {
scanf("%d%d%d",&x,&y,&z);
Status ans = Ask(1,cnt,x,y,1);
printf("%d\n",ans.ans[z] % MO);
}
}
return 0;
}