题目大意
你需要兹瓷对序列的三种操作:
1、区间加上一个数。2、区间取相反数。3、区间任意取k个数的积的和(k<=20)
线段树
我们需要维护两个标记,当线段树要维护多个标记时,如何处理标记之间的影响和如何下传标记成了问题。
这里推荐一下来自Fuxey的处理标记的有效原则
先看另一道题有趣的有趣的家庭菜园
在那道题中,线段树需要同时维护取max和加法标记。我们规定的执行顺序是先做取max再做加法。
没错,解决多标记问题,我们需要定一个标记优先级与执行顺序。那么每当要打一个新标记时,都要考虑对其执行顺序后的标记影响。处理好这一点就能解决问题。
同时,下传标记时,也应当按照执行顺序下传标记。
常见的标记还有set和add,执行顺序是先set再add。
JZOJ上还有一道题需要维护乘法和加法标记,我没有打,这题是3469,cty大爷写了题解数列
本题中,执行顺序是先取相反数再加法。
因为k<=20,可以想到保存dp数组f[i]表示在当前区间选取i个的积的和。
那么,当进行取相反操作时,因为其执行优先级比加法标记高,所以要将加法标记也取反。然后,对f的影响显然是
f[i]∗(−1)i
加法操作呢?来考虑f[i]的改变。
设加的是x,区间长度是len。
那么大概是一个(…+x)(…+x)……(…+x)
拆括号,枚举含有j个x,那么就是
xj∗f[i−j]∗Cjlen−i+j
那个组合数的含义?因为有j个x,这j个x所在括号另一项的可能性是什么呢?i-j个已经确定,还有len-i+j个未确定,要选出j个。
于是两个操作都解决了。
现在,我们还需要一个合并操作——将两个区间信息合并。
也很容易,枚举分别选i个j个相乘加进i+j里。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=50000+10,mo=19940417;
struct dong{
int f[21],len;
} tree[maxn*4];
int ad[maxn*4];
bool bz[maxn*4];
int a[maxn],c[maxn][21];
int i,j,k,l,t,n,m;
char ch;
dong ans;
void merge(dong a,dong b,dong &c){
c.len=a.len+b.len;
int i,j;
fo(i,0,20) c.f[i]=0;
fo(i,0,min(a.len,20))
fo(j,0,min(b.len,20))
if (i+j<=20) c.f[i+j]=(c.f[i+j]+(ll)a.f[i]*b.f[j]%mo)%mo;
}
void add(int p,int x){
int i,j,k;
fd(i,min(tree[p].len,20),0){
k=x;
fd(j,i-1,0){
tree[p].f[i]=(tree[p].f[i]+(ll)tree[p].f[j]*c[tree[p].len-j][i-j]%mo*k%mo)%mo;
tree[p].f[i]=(tree[p].f[i]+mo)%mo;
k=(ll)k*x%mo;
}
}
ad[p]=(ad[p]+x)%mo;
}
void rev(int p){
int i;
fo(i,0,min(tree[p].len,20)){
tree[p].f[i]*=(i%2)?-1:1;
tree[p].f[i]=(tree[p].f[i]+mo)%mo;
}
bz[p]^=1;
ad[p]=-ad[p];
}
void down(int p){
if (bz[p]){
rev(p*2);
rev(p*2+1);
bz[p]=0;
}
if (ad[p]){
add(p*2,ad[p]);
add(p*2+1,ad[p]);
ad[p]=0;
}
}
void change1(int p,int l,int r,int a,int b,int x){
if (l==a&&r==b){
add(p,x);
return;
}
down(p);
int mid=(l+r)/2;
if (b<=mid) change1(p*2,l,mid,a,b,x);
else if (a>mid) change1(p*2+1,mid+1,r,a,b,x);
else{
change1(p*2,l,mid,a,mid,x);
change1(p*2+1,mid+1,r,mid+1,b,x);
}
merge(tree[p*2],tree[p*2+1],tree[p]);
}
void change2(int p,int l,int r,int a,int b){
if (l==a&&r==b){
rev(p);
return;
}
down(p);
int mid=(l+r)/2;
if (b<=mid) change2(p*2,l,mid,a,b);
else if (a>mid) change2(p*2+1,mid+1,r,a,b);
else{
change2(p*2,l,mid,a,mid);
change2(p*2+1,mid+1,r,mid+1,b);
}
merge(tree[p*2],tree[p*2+1],tree[p]);
}
void build(int p,int l,int r){
if (l==r){
tree[p].len=1;
tree[p].f[0]=1;
tree[p].f[1]=a[l];
return;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
merge(tree[p*2],tree[p*2+1],tree[p]);
}
void query(int p,int l,int r,int a,int b){
if (l==a&&r==b){
merge(ans,tree[p],ans);
return;
}
down(p);
int mid=(l+r)/2;
if (b<=mid) query(p*2,l,mid,a,b);
else if (a>mid) query(p*2+1,mid+1,r,a,b);
else{
query(p*2,l,mid,a,mid);
query(p*2+1,mid+1,r,mid+1,b);
}
}
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;
}
char get(){
char ch=getchar();
while (ch!='Q'&&ch!='I'&&ch!='R') ch=getchar();
return ch;
}
int main(){
freopen("sequence8.in","r",stdin);
n=read();m=read();
c[0][0]=1;
fo(i,1,n){
c[i][0]=1;
fo(j,1,20) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
}
fo(i,1,n) a[i]=read();
build(1,1,n);
fo(i,1,m){
ch=get();
j=read();k=read();
if (ch=='I'){
l=read();
change1(1,1,n,j,k,l);
}
else if (ch=='R') change2(1,1,n,j,k);
else{
l=read();
ans.len=0;
ans.f[0]=1;
query(1,1,n,j,k);
printf("%d\n",ans.f[l]);
}
}
}