题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2962

维护 sum[i] 表示选 i 个的乘积和,合并两个子树就枚举两边选多少,乘起来即可;

取反只需要把奇数个数的乘积和变成相反数即可;

关键是区间 + k:比如对于一个元素,原来是 a, b, c,+ k 变成 (a+k), (b+k), (c+k)

从每个括号里选 a,b,c 或者 k,如果选 j 个 k ,那么就是 sum[i] += sum[i-j] * k^j * C(len-(i-j), j),组合数表示从剩余的位置中选 j 个 k;

详细可以看这个博客:https://blog.csdn.net/qq_35866453/article/details/77998472

注意一下处理 rev 标记和 lzy 标记,顺序是先 reverse 再 add,更改 rev 标记时要把 lzy 标记取反;

调了一上午:

1.各种计算 sum 时,上限原来写的是20,又觉得不好,改成 len,然而应该是 min(len,20) ...

2.query 写得不好的话会 TLE ... 还是模仿了 Narh 的写法,感觉很优秀。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
int const xn=5e4+,mod=;
int n,q,cnt=,a[xn],lzy[xn<<],ans[xn];
ll c[xn][];
bool rev[xn<<];
char ch;
struct N{int ls,rs,len; ll sum[];}t[xn<<];
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=(ret<<)+(ret<<)+ch-'',ch=getchar();
return f?ret:-ret;
}
void init()
{
for(int i=;i<=n;i++)c[i][]=;
for(int i=;i<=n;i++)
for(int j=;j<=min(i,);j++)
c[i][j]=(c[i-][j]+c[i-][j-])%mod;
}
ll pls(ll a,ll b){a=a+b; while(a>=mod)a-=mod; while(a<)a+=mod; return a;}
void pushup(int x)
{
int ls=t[x].ls,rs=t[x].rs,lm=min(t[x].len,);
for(int i=;i<=lm;i++)//min!!
{
t[x].sum[i]=;
for(int j=;j<=i;j++)
t[x].sum[i]=pls(t[x].sum[i],(t[ls].sum[j]*t[rs].sum[i-j])%mod);
}
}
void ad(int x,int k)
{
int len=t[x].len,lm=min(len,);
for(int i=lm;i>=;i--)//min
for(int j=,pw=k;j<=i;j++,pw=((ll)pw*k)%mod)//
t[x].sum[i]=pls(t[x].sum[i],t[x].sum[i-j]*pw%mod*c[len-i+j][j]%mod);
lzy[x]=pls(lzy[x],k);
}
void re(int x)
{
int lm=min(t[x].len,);
for(int i=;i<=lm;i+=)t[x].sum[i]=pls(mod,-t[x].sum[i]);//min
rev[x]^=;
lzy[x]=pls(mod,-lzy[x]);
}
void pushdown(int x)
{
int ls=t[x].ls,rs=t[x].rs;
if(rev[x])re(ls),re(rs),rev[x]=;
if(lzy[x])ad(ls,lzy[x]),ad(rs,lzy[x]),lzy[x]=;
}
void build(int x,int l,int r)
{
t[x].sum[]=; t[x].len=r-l+;
if(l==r){t[x].sum[]=a[l]; return;}
t[x].ls=++cnt; t[x].rs=++cnt;
int ls=t[x].ls,rs=t[x].rs;
build(ls,l,mid); build(rs,mid+,r);
pushup(x);
}
void add(int x,int l,int r,int L,int R,int k)
{
if(l>=L&&r<=R){ad(x,k); return;}
pushdown(x);
int ls=t[x].ls,rs=t[x].rs;
if(mid>=L)add(ls,l,mid,L,R,k);
if(mid<R)add(rs,mid+,r,L,R,k);
pushup(x);
}
void reverse(int x,int l,int r,int L,int R)
{
if(l>=L&&r<=R){re(x); return;}
pushdown(x);
int ls=t[x].ls,rs=t[x].rs;
if(mid>=L)reverse(ls,l,mid,L,R);
if(mid<R)reverse(rs,mid+,r,L,R);
pushup(x);
}
/*
ll query(int x,int l,int r,int L,int R,int k)
{
int ls=t[x].ls,rs=t[x].rs;
if(l>=L&&r<=R)return t[x].sum[k];
pushdown(x);
if(mid>=R)return query(ls,l,mid,L,R,k);
else if(mid<L)return query(rs,mid+1,r,L,R,k);
else
{
ll ret=0;
for(int i=0;i<=k;i++)
ret=pls(ret,query(ls,l,mid,L,R,i)*query(rs,mid+1,r,L,R,k-i)%mod);
return ret;
}
}
*/
void query(int cr,int l,int r,int L,int R,int k)
{
if(l>=L&&r<=R)
{
int lm=min(k,r-l+);
for(int i=k;i>=;i--)
for(int j=;j<=lm&&j<=i;j++)//j<=i
ans[i]=((ll)ans[i]+t[cr].sum[j]*ans[i-j])%mod;
return;
} pushdown(cr);
if(L<=mid) query(t[cr].ls,l,mid,L,R,k);
if(mid<R) query(t[cr].rs,mid+,r,L,R,k);
} int main()
{
n=rd(); q=rd();
init();
for(int i=;i<=n;i++)a[i]=rd();
build(,,n);
ans[]=;
for(int i=,a,b,c;i<=q;i++)
{
cin>>ch;
a=rd(); b=rd();
if(ch!='R')c=rd();
if(ch=='I')c=pls(c,),add(,,n,a,b,c);
if(ch=='R')reverse(,,n,a,b);
if(ch=='Q')
// printf("%lld\n",query(1,1,n,a,b,c)%mod); {
for(int i=;i<=c;i++)ans[i]=;
query(,,n,a,b,c);
printf("%d\n",ans[c]);
} }
return ;
}

bzoj 2962 序列操作 —— 线段树的更多相关文章

  1. bzoj 2962 序列操作——线段树(卷积?)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2962 如果 _,_,_,…… 变成了 (_+k),(_+k),(_+k),…… ,计算就是在 ...

  2. [bzoj]2962序列操作

    [bzoj]2962序列操作 标签: 线段树 题目链接 题意 给你一串序列,要你维护三个操作: 1.区间加法 2.区间取相反数 3.区间内任意选k个数相乘的积 题解 第三个操作看起来一脸懵逼啊. 其实 ...

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

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

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

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

  5. bzoj 2962 序列操作

    2962: 序列操作 Time Limit: 50 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 有一个长度为n的序列, ...

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

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

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

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

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

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

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

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

随机推荐

  1. Codeforces603E - Pastoral Oddities

    Portal Description 初始时有\(n(n\leq10^5)\)个孤立的点,依次向图中加入\(m(m\leq3\times10^5)\)条带权无向边.使得图中每个点的度数均为奇数的边集是 ...

  2. maven 编译出错 Failed to execute goal org.apache.maven.plugins:maven-clean-plugin:2.5:clean

    eclipse在使用maven的tomcat控件编译java程序时,报错 Failed to execute goal org.apache.maven.plugins:maven-clean-plu ...

  3. msp430项目编程

    msp430中项目---LED数码管显示 1.数码管介绍 2.代码(直接使用引脚驱动) 3.代码(使用译码器驱动) 4.项目总结 msp430项目编程 msp430入门学习

  4. Object_C 定义全局宏的颜色时,报“Expected identifier”的错误

    在定义全局颜色宏的时候,为了整齐把空格删了,写在了同一行里,调用的时候,出错提示“Expected identifier”,如下: 如果宏定义如上那样的话,在调用的时候,会出现如下的问题: 百思不得解 ...

  5. linux 开机启动脚本或者服务

    https://blog.csdn.net/zhuchunyan_aijia/article/details/53811368

  6. C++ Virtual 关键字

    虚函数是C++中用于多态的机制.核心理念就是通过基类访问派生类定义的函数. 基类的析构含糊都必须是virtual的 虚函数只能借助于指针或者引用来达到多态的效果. 前提B类继承与A类 且foo()为虚 ...

  7. [转] OracleDataReader.Read()是否有值

    TongYu2009的原文地址 当你执行一次OracleDataReader.Read()是Bool型),注意是只读取一个!如果你的Select语句执行结果是空,或者所有的结果都已经读取完了则Orac ...

  8. 【APUE】文件I/O

    Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descri ...

  9. How do I know which version of Javascript I'm using?

    Click on this link to see which version your BROWSER is using: http://jsfiddle.net/Ac6CT/ You should ...

  10. ACM之数论数字根

    先来看一道杭电的数字根问题 此题的大大意是输入一个数.假设它不是一位的数字的话,那么我们就将它的每一位都相加,相加后假设还是两位或者很多其它的话那么我们继续取出它的每一位数字进行相加.知道等到单个数字 ...