bzoj 2962: 序列操作
Posted yuzao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 2962: 序列操作相关的知识,希望对你有一定的参考价值。
Description
有一个长度为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的值。
Solution
注意到 (c) 比较小,可以直接维护一个 (O(20^2)) 的 (DP)
即设 (f[i]) 表示选了 (i) 个数相乘的方案
用线段树维护
合并的话就是 (f[o][i]=sum_{j=0}^{i} f[ls][j]*f[rs][i-j])
考虑修改
加上 (x),其实就是把所有的 (a*b*c) 变成 ((a+x)*(b+x)*(c+x))
用展开就是:
(a*b*c+(ab+bc+ac)*x+(a+b+c)*x^2+x^3)
那么 (f[i]) 修改之后的值就是
(f[i]=sum_{j=1}^{i}f[j]*C_{len-j}^{i-j}*x^{i-j})
(len) 是区间长度,因为这个多项式是有 (len) 项的,所以要乘以组合数
对于变成相反数的操作,要注意:
不仅维护的值要变,标记也要取反
#include <bits/stdc++.h>
#define ls (o<<1)
#define rs (o<<1|1)
using namespace std;
const int N=50010,mod=19940417;
inline int gi(){
register int str=0;register char ch=getchar();bool fg=0;
while(ch>'9' || ch<'0'){if(ch=='-')fg=1;ch=getchar();}
while(ch>='0' && ch<='9')str=(str<<3)+(str<<1)+ch-48,ch=getchar();
return fg?-str:str;
}
int n,Q,c[N][25],f[25],la[N*4];bool rev[N*4];
struct data{int b[25];data(){memset(b,0,sizeof(b));b[0]=1;}}tr[N*4];
inline void priwork(){
for(int i=0;i<=n;i++){
c[i][0]=1;
for(int j=min(20,i);j>=1;j--)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
}
inline data merge(data x,data y){
data r;
for(int i=1;i<=20;i++)
for(int j=0;j<=i;j++)
r.b[i]=(r.b[i]+1ll*x.b[j]*y.b[i-j])%mod;
return r;
}
inline void build(int l,int r,int o){
if(l==r){tr[o].b[1]=gi()%mod;return ;}
int mid=(l+r)>>1;
build(l,mid,ls);build(mid+1,r,rs);
tr[o]=merge(tr[ls],tr[rs]);
}
inline void Rec1(int o,int t,int l,int r){
la[o]=(la[o]+t)%mod;
for(int len=r-l+1,i=min(len,20);i>=1;i--){
f[i]=0;
for(int j=i,tp=1;j>=0;j--,tp=1ll*tp*t%mod)
f[i]=(f[i]+1ll*tr[o].b[j]*c[len-j][i-j]%mod*tp)%mod;
}
for(int i=min(r-l+1,20);i>=1;i--)tr[o].b[i]=f[i];
}
inline void Rec2(int o){
rev[o]^=1;la[o]=(mod-la[o])%mod;
for(int i=1;i<=20;i+=2)tr[o].b[i]=(mod-tr[o].b[i])%mod;
}
inline void pushdown(int o,int l,int r){
if(rev[o])Rec2(ls),Rec2(rs),rev[o]=0;
if(la[o]){
int mid=(l+r)>>1;
Rec1(ls,la[o],l,mid),Rec1(rs,la[o],mid+1,r),la[o]=0;
}
}
inline void add(int l,int r,int o,int sa,int se,int t){
if(sa<=l && r<=se){Rec1(o,t,l,r);return ;}
pushdown(o,l,r);
int mid=(l+r)>>1;
if(se<=mid)add(l,mid,ls,sa,se,t);
else if(sa>mid)add(mid+1,r,rs,sa,se,t);
else add(l,mid,ls,sa,mid,t),add(mid+1,r,rs,mid+1,se,t);
tr[o]=merge(tr[ls],tr[rs]);
}
inline void Modify(int l,int r,int o,int sa,int se){
if(sa<=l && r<=se){Rec2(o);return ;}
pushdown(o,l,r);
int mid=(l+r)>>1;
if(se<=mid)Modify(l,mid,ls,sa,se);
else if(sa>mid)Modify(mid+1,r,rs,sa,se);
else Modify(l,mid,ls,sa,mid),Modify(mid+1,r,rs,mid+1,se);
tr[o]=merge(tr[ls],tr[rs]);
}
inline data qry(int l,int r,int o,int sa,int se){
if(sa<=l && r<=se)return tr[o];
pushdown(o,l,r);
int mid=(l+r)>>1;data ret;
if(se<=mid)ret=qry(l,mid,ls,sa,se);
else if(sa>mid)ret=qry(mid+1,r,rs,sa,se);
else ret=merge(qry(l,mid,ls,sa,mid),qry(mid+1,r,rs,mid+1,se));
tr[o]=merge(tr[ls],tr[rs]);
return ret;
}
int main()
{
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
cin>>n>>Q;priwork();
build(1,n,1);
char s[2];int l,r;
while(Q--){
scanf("%s%d%d",s,&l,&r);
if(s[0]=='I')add(1,n,1,l,r,gi()%mod);
else if(s[0]=='R')Modify(1,n,1,l,r);
else printf("%d
",(qry(1,n,1,l,r).b[gi()]+mod)%mod);
}
return 0;
}
以上是关于bzoj 2962: 序列操作的主要内容,如果未能解决你的问题,请参考以下文章