[清华集训]序列操作
写了这么多数据结构题,一半左右是线段树
线段树真是优秀啊
题意
有一个长度为n的序列,有三个操作:
1.I a b c
表示将\([a,b]\)这一段区间的元素集体增加\(c\)
2.R a b
表示将\([a,b]\)区间内所有元素变成相反数
3.Q a b c
表示询问\([a,b]\)这一段区间中选择\(c\)个数相乘的所有方案的和\(\mod 19940417\)的值(\(1 \leq c \leq 20\))
首先考虑维护哪些信息
显然需要维护一个数组表示答案
然后考虑合并
显然
\(f[rt][n]=f[ls][n]+f[rs][n]+\sum_{i=1}^{n-1}{f[ls][i]*f[rs][n-i]}\)
再考虑维护哪些标记
区间加法,区间反转
考虑标记的维护
显然先维护反转标记后维护加法标记
最后考虑修改答案
区间反转只需把乘奇数个数的答案反转
区间加法类似二项式定理可以推出(好吧其实和二项式定理没有什么关系)
\(f[rt][n]=f[rt][n]+ \sum_{i=1}^{n-1}{f[rt][j]*C_{size-j}^{i-j}* k^i } + C_{size}^i * k^n\)
因为前面的值会影响后面的,所以从后往前更新
#include<bits/stdc++.h>
using namespace std;
#define gc c=getchar()
#define r(x) read(x)
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
template<typename T>
inline void read(T&x){
x=0;T k=1;char gc;
while(!isdigit(c)){if(c=='-')k=-1;gc;}
while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}
const int N=1e5+7;
const int p=19940417;
ll C[N][21];
struct Seg{
bool tag1;
ll tag2,a[21];
int siz;
Seg(){
tag1=0;
tag2=0;
memset(a,0,sizeof(a));
siz=1;
}
inline const ll& operator [](const int &x)const{
return a[x];
}
inline ll& operator [](const int &x){
return a[x];
}
inline void reverse(){
tag1^=1;
if(tag2)tag2=p-tag2;
for(int i=1;i<20;i+=2)if(a[i])a[i]=p-a[i];
}
inline void increase(int x){
(tag2+=x)%=p;
for(int i=min(20,siz);i;--i){
ll Tmp=x;
for(int j=i-1;j;--j,(Tmp*=x)%=p){
(a[i]+=Tmp*a[j]%p*C[siz-j][i-j]%p)%=p;
}
(a[i]+=C[siz][i]*Tmp)%=p;
}
}
}tr[N<<2];
inline Seg operator + (const Seg &a,const Seg &b){
Seg ret;
ret[0]=1;
ret.siz=a.siz+b.siz;
for(int i=1;i<=min(ret.siz,20);++i){
ret[i]=(a[i]+b[i])%p;
for(int j=1;j<i;++j){
(ret[i]+=a[j]*b[i-j])%=p;
}
}
return ret;
}
inline void update(int rt){
tr[rt]=tr[ls]+tr[rs];
}
inline void pushdown(int rt){
if(tr[rt].tag1){
tr[ls].reverse();
tr[rs].reverse();
tr[rt].tag1=0;
}
if(tr[rt].tag2){
tr[ls].increase(tr[rt].tag2);
tr[rs].increase(tr[rt].tag2);
tr[rt].tag2=0;
}
}
int a[N];
void build(int rt,int l,int r){
if(l==r){
tr[rt][0]=1;
tr[rt][1]=a[l];
return ;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
update(rt);
}
void reverse(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y){
tr[rt].reverse();
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(x<=mid)reverse(ls,l,mid,x,y);
if(y>mid)reverse(rs,mid+1,r,x,y);
update(rt);
}
void increase(int rt,int l,int r,int x,int y,int v){
if(x<=l&&r<=y){
tr[rt].increase(v);
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(x<=mid)increase(ls,l,mid,x,y,v);
if(y>mid)increase(rs,mid+1,r,x,y,v);
update(rt);
}
Seg query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return tr[rt];
pushdown(rt);
int mid=(l+r)>>1;
if(y<=mid)return query(ls,l,mid,x,y);
else if(x>mid)return query(rs,mid+1,r,x,y);
else return query(ls,l,mid,x,y)+query(rs,mid+1,r,x,y);
}
inline void pre(int n){
for(int i=0;i<=n;++i){
C[i][0]=C[i][i]=1;
for(int j=1;j<=min(i-1,20);++j){
C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
}
}
}
int main(){
int n,q;r(n),r(q);pre(n);
for(int i=1;i<=n;++i)r(a[i]),a[i]=(a[i]%p+p)%p;
build(1,1,n);
while(q--){
char opt[5];scanf("%s",opt);
int l,r,x;r(l),r(r);
switch(opt[0]){
case 'I':{r(x);x=(x%p+p)%p;increase(1,1,n,l,r,x);break;}
case 'R':{reverse(1,1,n,l,r);break;}
case 'Q':{r(x),printf("%lld\n",query(1,1,n,l,r)[x]);break;}
}
}
}