题目链接
有一个长度为nn的序列,有三个操作:
I a b c表示将[a,b][a,b]这一段区间的元素集体增加cc;
R a b表示将[a,b][a,b]区间内所有元素变成相反数;
Q a b c表示询问[a,b][a,b]这一段区间中选择cc个数相乘的所有方案的和\mod 19940417mod19940417的值。
输入格式:
第一行两个数n, qn,q表示序列长度和操作个数。
第二行nn个非负整数,表示序列。
接下来qq行每行输入一个操作I a b c或者R a b或者Q a b c,意义如题目描述。
输出格式:
对于每个询问,输出选出cc个数相乘的所有方案的和\mod 19940417mod19940417的值。
输入样例#1:
5 5
1 2 3 4 5
I 2 3 1
Q 2 4 2
R 1 5
I 1 3 -1
Q 1 5 1
输出样例#1:
40
19940397
样例说明:
做完第一个操作序列变为1 3 4 4 5。
第一次询问结果为3 \times 4+3 \times 4+4 \times 4=403×4+3×4+4×4=40。
做完R操作变成-1 -3 -4 -4 -5。
做完I操作变为-2 -4 -5 -4 -5。
第二次询问结果为-2-4-5-4-5=-20−2−4−5−4−5=−20。
数据范围:
对于100%的数据,n \leq 50000, q \leq 50000n≤50000,q≤50000,初始序列的元素的绝对值\leq 109≤109,保证[a,b][a,b]是一个合法区间,I操作中|c| \leq 109∣c∣≤109,Q操作中1 \leq c \leq \min(b-a+1,20)1≤c≤min(b−a+1,20)。
线段树的操作真的多……可以维护好多东西啊。感觉好像只考线段树,还挺激动的,结果自己推一推还是推不动……学习了大佬博客,那个公式推导是真的精髓,tql。
自己要走的路还很长呢,多学学这些大佬的思路。
#include
typedef long long ll;
const int N=50007;
const int M=500007;
const int mod=19940417;
#define lc o<<1
#define rc o<<1|1
int n,q,rev[M],a[N];
ll lazy[M],c[N][21],p[21];
struct node{
ll f[21];
}ans[M];
node merge(node x,node y)
{
node z;
for(int i=0;i<=20;i++)
{
z.f[i]=0;
for(int j=0;j<=i;j++)z.f[i]=(z.f[i]+x.f[j]*y.f[i-j]%mod)%mod;
}
return z;
}
void push_up(int o)
{
ans[o]=merge(ans[lc],ans[rc]);
}
void build(int o,int l,int r)
{
if(l==r)
{
ans[o].f[0]=1;
ans[o].f[1]=(a[l]%mod+mod)%mod;
return;
}
int mid=l+r>>1;
build(lc,l,mid);build(rc,mid+1,r);
push_up(o);
}
void change(int o,int len,ll v)
{
p[0]=1;
for(int i=1;i<=20;i++)p[i]=(p[i-1]*v)%mod;//求出每一项系数
for(int i=20;i>=0;i--)
for(int j=0;j*p[i-j]%mod*c[len-j][i-j]%mod)%mod;
}
void cle(int o)
{
for(int i=0;i<=20;i++)if(i%2)ans[o].f[i]=(mod-ans[o].f[i])%mod;
}
void push_down(int o,int l,int r)
{
if(rev[o])
{
rev[lc]^=1;
lazy[lc]=(mod-lazy[lc])%mod;
cle(lc);
rev[rc]^=1;
lazy[rc]=(mod-lazy[rc])%mod;
cle(rc);
rev[o]^=1;
}
if(lazy[o])
{
lazy[lc]=(lazy[o]+lazy[lc])%mod;
lazy[rc]=(lazy[o]+lazy[rc])%mod;
int mid=l+r>>1;
change(lc,mid-l+1,lazy[o]);
change(rc,r-mid,lazy[o]);
lazy[o]=0;
}
}
void update(int o,int l,int r,int nl,int nr,ll v)
{
if(nl==l&&r==nr)
{
lazy[o]=(lazy[o]+v)%mod;
change(o,r-l+1,v);
return;
}
push_down(o,l,r);
int mid=l+r>>1;
if(nr<=mid)update(lc,l,mid,nl,nr,v);
else if(nl>mid)update(rc,mid+1,r,nl,nr,v);
else update(lc,l,mid,nl,mid,v),update(rc,mid+1,r,mid+1,nr,v);
push_up(o);
}
void Rev(int o,int l,int r,int nl,int nr)
{
if(nl==l&&r==nr)
{
rev[o]^=1;
cle(o);
lazy[o]=(mod-lazy[o])%mod;
return;
}
push_down(o,l,r);
int mid=l+r>>1;
if(nr<=mid)Rev(lc,l,mid,nl,nr);
else if(nl>mid)Rev(rc,mid+1,r,nl,nr);
else Rev(lc,l,mid,nl,mid),Rev(rc,mid+1,r,mid+1,nr);
push_up(o);
}
node query(int o,int l,int r,int nl,int nr)
{
if(nl==l&&r==nr)return ans[o];
push_down(o,l,r);
int mid=l+r>>1;
if(nr<=mid)return query(lc,l,mid,nl,nr);
else if(nl>mid)return query(rc,mid+1,r,nl,nr);
else return merge(query(lc,l,mid,nl,mid),query(rc,mid+1,r,mid+1,nr));
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&q);
char op[10];int x,y;ll z;
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
c[0][0]=1;
for(int i=1;i<=n;i++)//递推求组合数
{
c[i][0]=1;
for(int j=1;j<=20;j++)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
build(1,1,n);
while(q--)
{
scanf("%s",op);
if(op[0]=='I')//增加
{
scanf("%d%d%lld",&x,&y,&z);
z=(z%mod+mod)%mod;
update(1,1,n,x,y,z);
}
else if(op[0]=='R')//变成相反数
{
scanf("%d%d",&x,&y);
Rev(1,1,n,x,y);
}
else
{
scanf("%d%d%lld",&x,&y,&z);
printf("%lld\n",query(1,1,n,x,y).f[z]);
}
}
return 0;
}
又是线段树维护的奇奇怪怪的东西