题意
给一个数组a,一开始的值全为0。一共有三个操作:
1. 对区间[L,R]的每个数都加上w。
2. 将数组a用其前缀和数组代替。
3. 将询问区间[L,R]的区间和。
题解
首先我们可以知道假如对一个点进行+1的操作,那么做s次前缀和之后的结果为:
那么假如我需要计算1这个位置+1取s次前缀和之后对4的贡献,那么答案就是
C(s+2,3)
C
(
s
+
2
,
3
)
。由于询问是一个区间那么就可以表示成这个表上的一段区间,例如询问[L,R],那么答案就是
ΣC(s−1+i,i)(L<=i<=R)
Σ
C
(
s
−
1
+
i
,
i
)
(
L
<=
i
<=
R
)
,然后我们可以将这个求和表示成
C(s+R,R)−C(s+L−1,L−1)
C
(
s
+
R
,
R
)
−
C
(
s
+
L
−
1
,
L
−
1
)
。由于更新是一个区间,因此我们可以将询问区间[l,r],变成两个修改操作,首先对l上的值+w然后对r+1上的值-w。这样就可以O(1)的查询某个区间对当前询问区间答案的贡献了。
AC代码
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define mod 998244353
#define N 100010
typedef long long ll;
ll l[N*2],r[N*2],w[N*2],s[N*2];
ll f[N*2],inv[N*2];
ll qmi(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b%2)ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
ll C(ll n,ll m)
{
if(m<0)return 0;
return f[n]*inv[m]%mod*inv[n-m]%mod;
}
ll query(ll L,ll R,ll s,ll start)
{
L-=start;R-=start;
return ((C(s-1+R,R)-C(s-1+L-1,L-1))%mod+mod)%mod;
}
int main()
{
ll T;
scanf("%lld",&T);
f[0]=inv[1]=1;
for(ll i=1;i<N*2;i++)f[i]=f[i-1]*i%mod;
for(ll i=0;i<N*2;i++)inv[i]=qmi(f[i],mod-2);
while(T--)
{
memset(s,0,sizeof(s));
ll n,m,tot=0;
scanf("%lld%lld",&n,&m);
for(ll step=0;step<m;step++)
{
ll op;
scanf("%lld",&op);
if(op==1)scanf("%lld%lld%lld",&l[tot],&r[tot],&w[tot]),tot++;
if(op==2)s[tot-1]++;
if(op==3)
{
ll L,R;
scanf("%lld%lld",&L,&R);
ll now=0,ans=0;
for(ll i=tot-1;i>=0;i--)
{
now+=s[i];
if(l[i]<=R)ans=(ans+w[i]*query(max(L,l[i]),R,now+2,l[i])%mod)%mod;
if(r[i]+1<=R)ans=(ans-w[i]*query(max(L,r[i]+1),R,now+2,r[i]+1)%mod+mod)%mod;
}
printf("%lld\n",ans);
}
}
}
}