题目大意:

  有一个长度为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的值。

思路:

  显然需要用线段树维护一个数组sum[c]表示选c个数相乘的方案总和。合并的时候只要枚举c,然后枚举左边选几个数然后和右边的乘起来累加进去就好了。取反实际上就是把所有c为奇数的取反,关键是区间加。对于[l,r],区间+x,那么枚举c。注意到形式是这样的: newsum[c]=Σ(a1+x)(a2+x)...(ac+x),然后按照x的次数合并同类项,可以发现这个东西可以通过x^k,组合数C(r-l+1,k)和oldsum[c-k]得到答案。k为x的次数。其余就是普通的线段树了。注意:推标记时要注意顺序。

代码:

 #include<cstdio>
#include<cstring>
#include<iostream>
#define N 150000
#define mod 19940417
#define ll long long
using namespace std; int lazy[N],zhs[N][],size[N];
struct node{int sum[];}ans[N];
bool rev[N]; int read()
{
int x=,y=;
char ch=getchar();
while (ch<'' || ch>'') {if (ch=='-') y=-;ch=getchar();}
while (ch>='' && ch<='') {x=x*+ch-;ch=getchar();}
return x*y;
} void add(int &x,int y)
{
x+=y;
if (x>=mod) x-=mod;
} void up_date(int k)
{
for (int i=;i<=;i++)
{
ans[k].sum[i]=;
for (int j=;j<i;j++) add(ans[k].sum[i],(ll)ans[k<<].sum[j]*ans[k<<|].sum[i-j]%mod);
add(ans[k].sum[i],ans[k<<].sum[i]);add(ans[k].sum[i],ans[k<<|].sum[i]);
}
} void ins(int k,int val)
{
add(lazy[k],val);
for (int i=;i;i--)
{
int x=val,j;
for (j=i-;j;j--,x=(ll)x*val%mod)
add(ans[k].sum[i],(ll)x*ans[k].sum[j]%mod*zhs[size[k]-j][i-j]%mod);
add(ans[k].sum[i],(ll)x*zhs[size[k]][i]%mod);
}
} void turn(int k)
{
int i; rev[k]^=;
if (lazy[k]) lazy[k]=mod-lazy[k];
for (i=;i>;i-=) if (ans[k].sum[i]) ans[k].sum[i]=mod-ans[k].sum[i];
} void build(int l,int r,int cur)
{
size[cur]=r-l+;
if (l==r) {ans[cur].sum[]=read()%mod;return;}
int mid=(l+r)>>;
build(l,mid,cur<<),build(mid+,r,cur<<|),up_date(cur);
} void push_down(int k)
{
if (rev[k]) turn(k<<),turn(k<<|),rev[k]=;
if (lazy[k])ins(k<<,lazy[k]),ins(k<<|,lazy[k]),lazy[k]=;
} void fan(int l,int r,int k,int x,int y)
{
if (l==x && r==y){ turn(k); return; }
int mid=l+r>>; push_down(k);
if (y<=mid) fan(l,mid,k<<,x,y);
else if (x>mid) fan(mid+,r,k<<|,x,y);
else fan(l,mid,k<<,x,mid),fan(mid+,r,k<<|,mid+,y);
up_date(k);
} void jia(int L,int R,int cur,int l,int r,int val)
{
if (l==L && r==R) {ins(cur,val);return;}
int mid=(L+R)>>; push_down(cur);
if (r<=mid) jia(L,mid,cur<<,l,r,val);
else if (l>mid) jia(mid+,R,cur<<|,l,r,val);
else jia(L,mid,cur<<,l,mid,val),jia(mid+,R,cur<<|,mid+,r,val);
up_date(cur);
} node ask(int L,int R,int cur,int l,int r,int val)
{
if (l==L && r==R) return ans[cur];
int mid=(L+R)>>; push_down(cur);
if (l>mid) return ask(mid+,R,cur<<|,l,r,val);
else if (r<=mid) return ask(L,mid,cur<<,l,r,val);
else
{
node x=ask(L,mid,cur<<,l,mid,val),y=ask(mid+,R,cur<<|,mid+,r,val),t;
for (int i=;i<=val;i++)
{
t.sum[i]=(x.sum[i]+y.sum[i])%mod;
for (int j=;j<i;j++) add(t.sum[i],(ll)x.sum[j]*y.sum[i-j]%mod);
}
return t;
}
} int main()
{
int n=read(),m=read(),i,j;
zhs[][]=;
for (i=;i<=n;i++)
{
zhs[i][]=;
for (j=;j<=i && j<=;j++) zhs[i][j]=(zhs[i-][j-]+zhs[i-][j])%mod;
}
build(,n,);
while (m--)
{
char ch=getchar();
while (ch<'A' || ch>'Z') ch=getchar();
if (ch=='I')
{
int x=read(),y=read(),z=read()%mod;
if (z<) z+=mod; jia(,n,,x,y,z);
}
if (ch=='R')
{
int x=read(),y=read();
fan(,n,,x,y);
}
if (ch=='Q')
{
int x=read(),y=read(),z=read();
printf("%d\n",ask(,n,,x,y,z).sum[z]);
}
}
return ;
}

bzoj2962 序列操作 题解的更多相关文章

  1. [bzoj2962]序列操作_线段树_区间卷积

    序列操作 bzoj-2962 题目大意:给定一个n个数的正整数序列,m次操作.支持:1.区间加:2.区间取相反数:3.区间求选c个数的乘积和. 注释:$1\le n,m\le 5\cdot 10^4$ ...

  2. bzoj2962 序列操作

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

  3. BZOJ1858[Scoi2010]序列操作 题解

    题目大意: 有一个01序列,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0:1 a b 把[a, b]区间内的所有数全变成1:2 a b 把[a,b]区间 ...

  4. BZOJ1858:[SCOI2010]序列操作——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=1858 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于 ...

  5. 2019.01.04 bzoj2962: 序列操作(线段树+组合数学)

    传送门 线段树基础题. 题意:要求维护区间区间中选择ccc个数相乘的所有方案的和(c≤20c\le20c≤20),支持区间加,区间取负. 由于c≤20c\le20c≤20,因此可以对于每个线段树节点可 ...

  6. 【BZOJ2962】序列操作(线段树)

    [BZOJ2962]序列操作(线段树) 题面 BZOJ 题解 设\(s[i]\)表示区间内选择\(i\)个数的乘积的和 考虑如何向上合并? \(s[k]=\sum_{i=0}^klson.s[i]*r ...

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

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

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

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

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

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

随机推荐

  1. Python 文件处理

    文件夹: 得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd() 返回指定目录下的所有文件和目录名:os.listdir() 函数用来删除一个文件:os.remove() 删 ...

  2. max file descriptors [4096] for elasticsearch process likely too low, increase to at least [65536]

    在/etc/syscurity/limits.conf 加入以下两行: elastic hard nofile 65536 elastic soft nofile  65536 #备注:elastic ...

  3. Generic Access Profile

    转自:https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile ​​Assigned numbe ...

  4. php获取一维,二维数组长度的方法(有实例)

    在php中获取数组长度方法很简单,php为我们提供了两个函数可以计算一维数组长度,如count,sizeof都可以直接统计数组长度哦,下面我们来看几个实例吧.php如何获取数组的长度,使用php函数c ...

  5. 【131202】SQL

    SELECT TOP 2 * FROM Persons SELECT *FROM PersonsWHERE ROWNUM <= 5   SELECT * FROM Persons WHERE C ...

  6. Java Socket编程示例

    一.Socket简介: 1.什么是Socket 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket ...

  7. Linux/centos下安装riak

    必备的组件: gccgcc-c++glibc-develmakepam-devel 使用yum安装相关组件 sudo yum install gcc gcc-c++ glibc-devel make ...

  8. JqueryEasyUI 解决IE下加载时页面错乱的问题 分类: JavaScript JqueryEasyUI 2014-09-20 09:50 545人阅读 评论(1) 收藏

    问题描述: 一直觉得jqueryeasyui在IE下的渲染效果不大好,尤其刚进入页面时的加载,页面会出现布局错乱,虽然是一闪而过,但是给用户的体验不好: 可以通过在页面onload时,增加一个遮罩层, ...

  9. 咱就入个门之NHibernate映射文件配置(二)

    上一篇主要介绍了NHibernate映射文件的基础配置,这篇我们介绍下NHibernate的一对多及多对一配置(文中我直接使用双向关联,即一和多两端都配置,开发中可以只使用一端),同时略带介绍下NHi ...

  10. [转]svn常用命令

    谢谢原作者:http://blog.sina.com.cn/s/blog_963453200101eiuq.html 1.检出svn  co  http://路径(目录或文件的全路径) [本地目录全路 ...