先离线所有操作 保留所有操作一和三 并求出对应前缀和的级数
所有对i级前缀和[l,r]加val的操作 分为对i级前缀和l加val r+1减val
所有对j级前缀和[l,r]的查询操作 分为对j+1级前缀和r点查询值 减去对j+1级前缀和l-1点查询值
对所有操作三 统计之前的所有更新操作对其的影响即可 复杂度500*n
using namespace std;
const ll M=998244353;
struct node
{
int tp;
int k;
int pos;
ll val;
int l;
int r;
};
node order[200010];
ll pre[200010],inv[200010];
int n,q,cnt,tot;
ll quickpow(ll a,ll b)
{
ll res;
res=1;
while(b>0)
{
if(b%2) res=(res*a)%M;
a=(a*a)%M,b/=2;
}
return res;
}
void init()
{
ll i;
pre[0]=1,inv[0]=1;
for(i=1;i<=200005;i++)
{
pre[i]=(i*pre[i-1])%M;
inv[i]=quickpow(pre[i],M-2);
}
}
ll getcnk(int nn,int kk)
{
if(kk>nn) return 0;
return (((pre[nn]*inv[kk])%M)*inv[nn-kk])%M;
}
ll gabs(ll x)
{
if(x<0) return -x;
return x;
}
ll solve(int pl,int pr,int k)
{
ll res;
int x,y,i;
res=0;
for(i=1;i<=tot;i++)
{
if(order[i].pos<=pl)
{
x=pl-order[i].pos;
y=k-order[i].k;
ll f=1,fuck=order[i].val;
if(fuck<0)
f=-1;
fuck=gabs(fuck);
res=(res-f*(fuck*getcnk(x+y,x)%M)+M)%M;
}
if(order[i].pos<=pr)
{
x=pr-order[i].pos;
y=k-order[i].k;
ll f=1,fuck=order[i].val;
if(fuck<0)
f=-1;
fuck=gabs(fuck);
res=(res+f*(fuck*getcnk(x+y,x)%M)+M)%M;
}
}
return (res+M)%M;
}
int main()
{
ll val;
int t,i,op,l,r;
init();
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&q);
cnt=0,tot=0;
while(q--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%lld",&l,&r,&val);
tot++;
order[tot].tp=1;
order[tot].k=cnt+1;
order[tot].pos=l;
order[tot].val=val;
tot++;
order[tot].tp=1;
order[tot].k=cnt+1;
order[tot].pos=r+1;
order[tot].val=-val;
}
else if(op==2) cnt++;
else
{
int L,R,K=cnt+1;
scanf("%d%d",&L,&R);
printf("%lld\n",solve(L-1,R,K+1));
}
}
}
return 0;
}