版权声明: https://blog.csdn.net/weixin_39792252/article/details/82415291
D Rikka with Prefix Sum
题意:给一个数组a,一开始的值全为0,一共有三个操作:
1. 对区间[L,R]的每个数都加上w。
2. 将数组a用其前缀和数组代替。
3. 将询问区间[L,R]的区间和。
假设初始状态是t = 0,每执行一次 2 就t++,这样考虑每个1操作对于后面的影响,考虑是路径的问题,就是组合数。
代码:
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <map>
#include <cassert>
#define ll long long
using namespace std;
const ll maxn = 5e6+7;
const ll mod = 998244353;
struct node{
ll t, where, w;
} A[maxn];
int len;
ll F[maxn + 10], inv[maxn + 10];
ll qpow(ll a, ll b){
ll ans = 1ll;
while(b){
if(b&1) ans = (ans*a)%mod;
a = (a*a)%mod;
b >>= 1;
}
return ans;
}
void init()
{
F[0] = 1ll;
for (ll i = 1; i <= maxn; i++) F[i] = (F[i-1]*i)%mod;
inv[maxn] = qpow(F[maxn], mod - 2ll); //费马小定理求 N!的逆元
for(ll i = maxn - 1; i >= 0; i--) inv[i] = (inv[i+1]*(i+1))%mod;
}
ll C(ll a , ll b){ return F[a]*inv[b]%mod*inv[a-b]%mod; }
ll query(ll where,ll t){
ll ans = 0;
for (int i = 1; i <= len; i++)
if (A[i].where <= where) ans = (ans + A[i].w*C(where - A[i].where + t - A[i].t - 1, t - A[i].t - 1))%mod;
return ans;
}
void solve(){
int n, m;
scanf("%d%d", &n, &m);
len = 0;
ll t = 0, l, r, w;
while(m--){
int op; scanf("%d", &op);
if(op == 1){
scanf("%lld%lld%lld", &l, &r, &w);
A[++len] = (node){ t-1, l, w };
A[++len] = (node){ t-1, r+1, (mod-w)%mod };
} else if (op == 2) t++;
else{
scanf("%lld%lld", &l, &r);
printf("%lld\n",(query(r, t+1) - query(l-1, t+1) + mod)%mod);
}
}
}
int main(){
init();
//freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}