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\n",(qry(1,n,1,l,r).b[gi()]+mod)%mod);
	}
	return 0;
}

posted @ 2018-07-14 22:01  PIPIBoss  阅读(193)  评论(0编辑  收藏  举报