29.牛客网多校第十场D 组合数+多次前缀和
题意:n个数,初始值为0,m个操作 n,m<=1e5 操作3次数小于500
操作1:区间l~r+=c
操作2:区间1~n每个数变成前缀和
操作3:查询l~r加法和
思路:首先我们强行模拟一下前缀和:
第0次前缀和 a1, a2 a3 a4
第1次前缀和 a1, a1+a2, a1+a2+a3, a1+a2+a3+a4
第2次前缀和 a1,2a1+a2 3a1+2a2+a3,4a1+3a2+2a3+a4
第3次前缀和 a1,3a1+a2 6a1+3a2+a3,10a1+6a2+3a3+a4
….
只考虑a1的系数会发现1:
1 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
1C(1,1) | 2 C(2,1) | 3 C(3,1) | 4 C(4,1) |
1 C(2,2) | 3 C(3,2) | 6 C(4,2) | 10 C(5,2) |
由发现1得到发现2
发现2:ay对第k次前缀和第x项的影响:C(k-1+y-x,k-1)
比如a1 k=3 的第4项,10a1=C(4-1+3-1 , 3-1)*a1=C(5,2)a1
发现3:
第二次前缀和的前缀和就等于第三次前缀和里的某一项
遇到操作1:
维护已经前缀和次数tm,位置pos,和值,
像差分数组一样维护差分a[l]+=x,a[r+1]-=x
到时候做一次前缀和便能得出结果tot+1
遇到操作3【L,R】
考虑之前每次操作1的贡献,修改ax 会对ax,ax+1.ax+2…ax+R造成影响
由发现3:这些影响的前缀和就是等于下一次前缀和的ax对aR的影响
由发现2:这个影响的计算O(1)解决
所以我们要多做两次前缀和才能得出答案,这就是tot+2的原因
至此,我们求出了1~R的加法和,要求L~R的就多算一遍1~L-1的就好了
#include<bits/stdc++.h>
#define ll long long
const int mod=998244353;
using namespace std;
struct fuck{
int pos,tm,w;
}a[200005];
ll jc[200005],jv[200005];
ll tot;//操作2次数
ll cnt;//操作1次数
ll qmod(ll a,ll b)
{
ll res=1;while(b)
{
if(b&1)res=res*a%mod;
a=a*a%mod;b=b>>1;
}return res;
}
ll getC(ll m,ll n)
{
return jc[m]*jv[n]%mod*jv[m-n]%mod;
}
ll solve(int R)
{
ll ans=0;
for(int i=1;i<=cnt;i++)//修改对R的影响
{
if(R>=a[i].pos)
(ans+= a[i].w*getC(R-a[i].pos+tot+2 -a[i].tm-1,tot+2 -a[i].tm-1) )%=mod;
//因为要算1~R的前缀和 所以tot+1
//因为区间+=c维护的是差分,所以要多求一次前缀和 tot+2
}
return ans;
}
int main()
{
jc[0]=jv[0]=1;
for(int i=1;i<=200000;i++)jc[i]=jc[i-1]*i%mod;
jv[200000]=qmod(jc[200000],mod-2);
for(int i=199999;i>=1;i--)jv[i]=jv[i+1]*(i+1)%mod;
//printf("%d\n",getC(7,2));
int T;scanf("%d",&T);
while(T--)
{
int n,m;scanf("%d %d",&n,&m);int op;cnt=tot=0;
while(m--)
{
scanf("%d",&op);
if(op==1)
{
int l,r,w;
scanf("%d %d %d",&l,&r,&w);
a[++cnt].pos=l;a[cnt].tm=tot;a[cnt].w=w%mod;
a[++cnt].pos=r+1;a[cnt].tm=tot;a[cnt].w=(mod-w)%mod;
}
else if(op==2)
tot++;
else
{
int l,r;scanf("%d %d",&l,&r);
printf("%lld\n",(solve(r)-solve(l-1)+mod)%mod);
}
}
}
}