传送门

线段树基础题。

题意:要求维护区间区间中选择ccc个数相乘的所有方案的和(c≤20c\le20c≤20),支持区间加,区间取负。


由于c≤20c\le20c≤20,因此可以对于每个线段树节点可以暴力维护212121个sumsumsum值,合并也很简单,是一个卷积的形式sumi=∑j=0isumjsumi−jsum_i=\sum_{j=0}^isum_jsum_{i-j}sumi​=∑j=0i​sumj​sumi−j​可以用FFT优化一波(滑稽。

区间取负并没有什么难度,对于sumisum_isumi​来说,如果iii是偶数就并没有什么影响,如果iii是奇数把sumisum_isumi​变成−sumi-sum_i−sumi​即可。

关键在于区间加。

考虑到区间加对每个sumsumsum的影响,我们把a1a2...ana_1a_2...a_na1​a2​...an​变成了(a1+x)(a2+x)...(an+x)(a_1+x)(a_2+x)...(a_n+x)(a1​+x)(a2​+x)...(an​+x),我们设这个区间长度为lenlenlen,那么有组合数学的方法可以将这个式子展开:newsumi=∑j=0iClen−ji−jxjoldsumjnewsum_{i}=\sum_{j=0}^iC_{len-j}^{i-j}x^joldsum_jnewsumi​=∑j=0i​Clen−ji−j​xjoldsumj​ 相当于是枚举每个括号里面xxx的个数来更新答案

然后就没啥了~~注意细节~~

代码:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
#define add(a,b) ((a)+(b)>=mod?(a)+(b)-mod:(a)+(b))
#define mul(a,b) ((ll)(a)*(b)%mod)
#define dec(a,b) ((a)>=(b)?(a)-(b):(a)-(b)+mod)
#define ri register int
using namespace std;
typedef long long ll;
const int mod=19940417,N=5e5+5;
int n,m,a[N],C[N][21];
inline int read(){
	int ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
struct Node{int l,r,sum[21],add;bool rev;Node(){l=r=add=rev=0;for(ri i=0;i<=20;++i)sum[i]=0;}}T[N<<2];
inline Node operator+(const Node&a,const Node&b){
	Node ret;
	ret.l=a.l,ret.r=b.r;
	for(ri i=0;i<=20;++i)for(ri j=0;j<=i;++j)ret.sum[i]=add(ret.sum[i],mul(a.sum[j],b.sum[i-j]));
	return ret;
}
inline void pushadd(int p,int v){
	T[p].add=add(T[p].add,v);
	for(ri i=min(T[p].r-T[p].l+1,20),len=T[p].r-T[p].l+1;~i;--i)for(ri j=i-1,mult=v;~j;--j,mult=mul(mult,v))
		T[p].sum[i]=add(T[p].sum[i],mul(C[len-j][i-j],mul(mult,T[p].sum[j])));
}
inline void pushrev(int p){
	T[p].rev^=1,T[p].add=dec(0,T[p].add);
	for(ri i=0;i<=20;++i)if(i&1)T[p].sum[i]=dec(0,T[p].sum[i]);
}
inline void pushdown(int p){
	if(T[p].rev)pushrev(lc),pushrev(rc),T[p].rev^=1;
	if(T[p].add)pushadd(lc,T[p].add),pushadd(rc,T[p].add),T[p].add=0;
}
inline void build(int p,int l,int r){
	T[p].l=l,T[p].r=r;
	if(T[p].l==T[p].r){T[p].sum[0]=1,T[p].sum[1]=a[l];return;}
	build(lc,l,mid),build(rc,mid+1,r),T[p]=T[lc]+T[rc];
}
inline void update(int p,int ql,int qr,int v){
	if(ql<=T[p].l&&T[p].r<=qr)return v?pushadd(p,v):pushrev(p);
	pushdown(p);
	if(qr<=mid)update(lc,ql,qr,v);
	else if(ql>mid)update(rc,ql,qr,v);
	else update(lc,ql,mid,v),update(rc,mid+1,qr,v);
	T[p]=T[lc]+T[rc];
}
inline Node query(int p,int ql,int qr){
	if(ql<=T[p].l&&T[p].r<=qr)return T[p];
	pushdown(p);
	if(qr<=mid)return query(lc,ql,qr);
	if(ql>mid)return query(rc,ql,qr);
	return query(lc,ql,mid)+query(rc,mid+1,qr);
}
inline void init(){
	for(ri i=0;i<=n;++i)C[i][0]=1;
	for(ri i=1;i<=n;++i){
		C[i][1]=i;
		for(ri j=2;j<=min(20,i);++j)C[i][j]=add(C[i-1][j],C[i-1][j-1]);
	}
	build(1,1,n);
}
int main(){
	n=read(),m=read();
	for(ri i=1;i<=n;++i)a[i]=(read()%mod+mod)%mod;
	init();
	while(m--){
		char s[2];
		scanf("%s",s);
		int l=read(),r=read(),v;
		if(s[0]=='I'){
			v=(read()%mod+mod)%mod;
			if(!v)continue;
			update(1,l,r,v);
		}
		else if(s[0]=='R')update(1,l,r,0);
		else cout<<query(1,l,r).sum[read()]<<'\n';
	}
	return 0;
}

2019.01.04 bzoj2962: 序列操作(线段树+组合数学)的更多相关文章

  1. 【题解】P4247 [清华集训]序列操作(线段树修改DP)

    [题解]P4247 [清华集训]序列操作(线段树修改DP) 一道神仙数据结构(DP)题. 题目大意 给定你一个序列,会区间加和区间变相反数,要你支持查询一段区间内任意选择\(c\)个数乘起来的和.对1 ...

  2. 【BZOJ2962】序列操作 线段树

    [BZOJ2962]序列操作 Description 有一个长度为n的序列,有三个操作1.I a b c表示将[a,b]这一段区间的元素集体增加c,2.R a b表示将[a,b]区间内所有元素变成相反 ...

  3. 【BZOJ-2962】序列操作 线段树 + 区间卷积

    2962: 序列操作 Time Limit: 50 Sec  Memory Limit: 256 MBSubmit: 678  Solved: 246[Submit][Status][Discuss] ...

  4. 【BZOJ-1858】序列操作 线段树

    1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1961  Solved: 991[Submit][Status ...

  5. BZOJ 1858: [Scoi2010]序列操作( 线段树 )

    略恶心的线段树...不过只要弄清楚了AC应该不难.... ---------------------------------------------------------------- #inclu ...

  6. 【bzoj1858】[Scoi2010]序列操作 线段树区间合并

    题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b ...

  7. Luogu P2572 [SCOI2010]序列操作 线段树。。

    咕咕了...于是借鉴了小粉兔的做法ORZ... 其实就是维护最大子段和的线段树,但上面又多了一些操作....QWQ 维护8个信息:1/0的个数(sum),左/右边起1/0的最长长度(ls,rs),整段 ...

  8. 洛谷$P2572\ [SCOI2010]$ 序列操作 线段树/珂朵莉树

    正解:线段树/珂朵莉树 解题报告: 传送门$w$ 本来是想写线段树的,,,然后神仙$tt$跟我港可以用珂朵莉所以决定顺便学下珂朵莉趴$QwQ$ 还是先写线段树做法$QwQ$? 操作一二三四都很$eas ...

  9. bzoj1858[Scoi2010]序列操作 线段树

    1858: [Scoi2010]序列操作 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 3079  Solved: 1475[Submit][Statu ...

随机推荐

  1. Java_10 继承

    1 继承的好处 继承的出现提高了代码的复用性,提高软件开发效率. 继承的出现让类与类之间产生了关系,提供了多态的前提. 2 继承的注意事项 在Java中,类只支持单继承,不允许多继承,也就是说一个类只 ...

  2. cgi,fast-cgi,php-cgi,php-fpm转载详解

    转载自:https://segmentfault.com/q/1010000000256516 首先,CGI是干嘛的?CGI是为了保证web server传递过来的数据是标准格式的,方便CGI程序的编 ...

  3. Bootstrap(2) 排版样式

    1.页面主体,Bootstrap 将全局 font-size 设置为 14px,line-height 行高设置为 1.428(即20px):<p>段落元素被设置等于 1/2 行高(即 1 ...

  4. how2j网站前端项目——天猫前端(第一次)学习笔记5

    收拾好心情,现在开始学习第5个页面——购物车页面! 一.结算按钮 这个还是比较简单的,我自己看着站长的样子模仿了一个: 有个地方不会做,就是全选前面的复选框,站长的框里面是白色的,我搞不来. 二.订单 ...

  5. 无法连接到localhost。其他信息:用户“sa”登录失败。原因:该用户被禁用。(Microsoft Sql Server,错误:18470).

    18470错误: 解决方案: 使用windows身份验证登录之后,选择安全性--->登录名--->sa--->右击--->属性: 右击选择属性进入属性页面: 选择状态,然后再登 ...

  6. [z]oracle优化http://jadethao.iteye.com/blog/1613943

    [sql] view plaincopy SQL> create table t as select 1 id,object_name from dba_objects; Table creat ...

  7. 2D情况下,复数的意义代表旋转

    4 x i x i = - 4 就是"4"在数轴上旋转了180度. 那么4 x i = 4i 就旋转了90度. 复数的意义就表示旋转 乘以-1,表示x正半轴的数,围绕原点,逆时针偏 ...

  8. Android.Study.Question

    1. NullPointerException 1.1 发生该异常的原因. 1.2 解决方法有哪几种? try-catch 2. Eclipse 中 debug/run 两个模式,run 是relea ...

  9. BZOJ1051或洛谷2341 [HAOI2006]受欢迎的牛

    BZOJ原题链接 洛谷原题链接 显然在一个强连通分量里的奶牛都可以相互喜欢,所以可以用\(tarjan\)求强连通并缩点. 要求明星奶牛必须被所有人喜欢,显然缩点后的图必须满足只有一个点没有出度,因为 ...

  10. AFNetworking HTTP响应头返回数据

    //发送验证码 NSLog(@"发送验证码"); AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationM ...