2019.01.04 bzoj2962: 序列操作(线段树+组合数学)
传送门
线段树基础题。
题意:要求维护区间区间中选择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=0isumjsumi−j可以用FFT优化一波(滑稽。
区间取负并没有什么难度,对于sumisum_isumi来说,如果iii是偶数就并没有什么影响,如果iii是奇数把sumisum_isumi变成−sumi-sum_i−sumi即可。
关键在于区间加。
考虑到区间加对每个sumsumsum的影响,我们把a1a2...ana_1a_2...a_na1a2...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=0iClen−ji−jxjoldsumj 相当于是枚举每个括号里面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: 序列操作(线段树+组合数学)的更多相关文章
- 【题解】P4247 [清华集训]序列操作(线段树修改DP)
[题解]P4247 [清华集训]序列操作(线段树修改DP) 一道神仙数据结构(DP)题. 题目大意 给定你一个序列,会区间加和区间变相反数,要你支持查询一段区间内任意选择\(c\)个数乘起来的和.对1 ...
- 【BZOJ2962】序列操作 线段树
[BZOJ2962]序列操作 Description 有一个长度为n的序列,有三个操作1.I a b c表示将[a,b]这一段区间的元素集体增加c,2.R a b表示将[a,b]区间内所有元素变成相反 ...
- 【BZOJ-2962】序列操作 线段树 + 区间卷积
2962: 序列操作 Time Limit: 50 Sec Memory Limit: 256 MBSubmit: 678 Solved: 246[Submit][Status][Discuss] ...
- 【BZOJ-1858】序列操作 线段树
1858: [Scoi2010]序列操作 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1961 Solved: 991[Submit][Status ...
- BZOJ 1858: [Scoi2010]序列操作( 线段树 )
略恶心的线段树...不过只要弄清楚了AC应该不难.... ---------------------------------------------------------------- #inclu ...
- 【bzoj1858】[Scoi2010]序列操作 线段树区间合并
题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b ...
- Luogu P2572 [SCOI2010]序列操作 线段树。。
咕咕了...于是借鉴了小粉兔的做法ORZ... 其实就是维护最大子段和的线段树,但上面又多了一些操作....QWQ 维护8个信息:1/0的个数(sum),左/右边起1/0的最长长度(ls,rs),整段 ...
- 洛谷$P2572\ [SCOI2010]$ 序列操作 线段树/珂朵莉树
正解:线段树/珂朵莉树 解题报告: 传送门$w$ 本来是想写线段树的,,,然后神仙$tt$跟我港可以用珂朵莉所以决定顺便学下珂朵莉趴$QwQ$ 还是先写线段树做法$QwQ$? 操作一二三四都很$eas ...
- bzoj1858[Scoi2010]序列操作 线段树
1858: [Scoi2010]序列操作 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 3079 Solved: 1475[Submit][Statu ...
随机推荐
- [LeetCode_105]Construct Binary Tree from Preorder and Inorder Traversal
题目链接 https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ 题意 由二叉 ...
- LRU缓存原理
LRU(Least Recently Used) LRU是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象. 采用LRU算法的缓存有两种:LrhCache和DisL ...
- Selenium + Python + Firefox
按网上教程搭建好环境后,执行下面的代码出现了错误: 测试代码如下: from selenium import webdriver driver=webdriver.Firefox() driver.g ...
- supervisor安装、使用详解
supervisor是用python写的一个进程管理工具,用来启动,重启,关闭进程. 1 supervisor的安装 pip install supervisor 2 supervisor的配置文件( ...
- Vs2015 c# 诊断工具查看程序的占用情况
windbg用着还不熟悉,dottrace 还要版权,着急查看程序的cpu 的使用情况,因为程序开启之后占用处理器资源较大,问题在哪里呢,于是点开了vs2015自带的诊断工具,以前偶尔打开过,没发现 ...
- Linux netstat
一.简介 二.语法 三.实例 1)查看TCP连接数 netstat -n | awk '/^tcp/ {++S[$NF]} END {for (a in S) print a, S[a]}'
- (转)android拨打电话崩溃6.0以上实时动态权限申请
文章转自:http://blog.csdn.net/qq_29988575/article/details/54909213 6.0以下手机正常,6.0以上的却崩溃 解决方法: targetSdkVe ...
- 使用开源的工具解析erspan流量
Decapsulation ERSPAN Traffic With Open Source Tools Posted on May 3, 2015 by Radovan BrezulaUpdated ...
- git的命令详解
# git三个区 + 工作区: 写代码的地方 + 暂存区: 暂时存储代码 + 仓库区: 代码提交到了仓库区,就生成一条历史记录(版本) 工作区===> 暂存区 ===> 仓库区 # git ...
- node.js 进程崩溃处理
process.on('uncaughtException', (err) => { console.error('有错误'); });