我是题目链接
参考博客:
1 2 3
利用组合数求和规律的找法
其他的思路注释在代码后;
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int p=998244353;
const int N=2e5+10;
int n,m,T,op,l,r,w,cnt;
struct orz
{
int x,pos,w;
} a[N];
ll fac[N],inv[N];
ll poww(ll x,int y)
{
x%=p;
ll ret=1;
while (y)
{
if (y&1) ret=ret*x%p;
x=x*x%p;
y>>=1;
}
return ret;
}
void pre()
{
fac[0]=1;
for (int i=1; i<N; i++) fac[i]=fac[i-1]*i%p;
inv[N-1]=poww(fac[N-1],p-2);
for (int i=N-2; i>=0; i--) inv[i]=inv[i+1]*(i+1)%p;
}
ll C(int a,int b)
{
if (b>a||b<0) return 0;
return fac[a]*inv[b]%p*inv[a-b]%p;
}
ll solve(int x,int y)
{
ll ret=0;
for (int i=1; i<=cnt; i++)
{
if (a[i].x<=x&&a[i].pos<=y)
ret=(ret+C(x-a[i].x+y-a[i].pos-1,x-a[i].x-1)*(ll)a[i].w%p)%p;
}
return ret;
}
int main()
{
scanf("%d",&T);
pre();
while (T--)
{
scanf("%d%d",&n,&m);
int now=1;
cnt=0;
while (m--)
{
scanf("%d",&op);
if (op==1)
{
scanf("%d%d%d",&l,&r,&w);
cnt++;
a[cnt].x=now-1;
a[cnt].pos=l;
a[cnt].w=w%p;
cnt++;
a[cnt].x=now-1;
a[cnt].pos=r+1;
a[cnt].w=-w%p;
}
else if (op==2) now++;
else
{
scanf("%d%d",&l,&r);
ll ans=((solve(now+1,r)-solve(now+1,l-1))%p+p)%p;//now+1的意思是C(5,1)是C(1,0)+C(2,0)+C(3,0)+C(4,0)+C(5,0)的值;
printf("%lld\n",ans);
}
}
}
return 0;
}
/*
1操作:
区间修给可以转换成单点修改,因为:
对原序列某一点进行修改,他只影响它右下侧的前缀和数组;
所以对l,r的区间修改,就相当于在(l,n)+w,在(r+1,n)-w;
各个操作1相互独立,分开来算,最后相加。
2操作:
记录下进行几次求前缀和的操作,也就是记录下操作了几次2;
为了3求和时,利用组合数求;
3操作:
我们发现在一个点+w之后,影响的是它右下角的所有点,
每个行的系数是杨辉三角的一列,所以如果在(i,j)点+w,
那么(x,y)点的系数为C(x-i+y-j-1,x-i-1)
*/
差分数组的介绍:
https://blog.csdn.net/jc514984625/article/details/53511438
有一个性质:
A[i]的差分数组是D[i], 而D[i]的前缀和sum[i]是A[i];
也就是:
A[i]: 1 2 4 5 7
D[i]: 1 1 2 1 2
sum[i]: 1 2 4 5 7