zoukankan      html  css  js  c++  java
  • Rikka with Prefix Sum(组合数学)

    Rikka with Prefix Sum

    题目描述

    Prefix Sum is a useful trick in data structure problems.
    For example, given an array A of length n and m queries. Each query gives an interval [l,r] and you need to calculate . How to solve this problem in O(n+m)? We can calculate the prefix sum array B in which Bi is equal to . And for each query, the answer is Br-Bl-1.
    Since Rikka is interested in this powerful trick, she sets a simple task about Prefix Sum for you:
    Given two integers n,m, Rikka constructs an array A of length n which is initialized by Ai = 0. And then she makes m operations on it.
    There are three types of operations:
    1. 1 L R w, for each index i ∈ [L,R], change Ai to A+ w.
    2. 2, change A to its prefix sum array. i.e., let A' be a back-up of A, for each i ∈ [1,n], change Ai to .
    3. 3 L R, query for the interval sum .
    Intput:

    The first line contains a single number t(1≤ t ≤ 3), the number of the testcases.
    For each testcase, the first line contains two integers n,m(1 ≤ n,m ≤ 10^5)
    And then m lines follow, each line describes an operation(1 ≤ L ≤ R≤ n, 0 ≤ w ≤ 10^9).
    The input guarantees that for each testcase, there are at most 500 operations of type 3.

    output:

    For each query, output a single line with a single integer, the answer modulo 998244353.

    test:

    Intput:

    1
    100000 7
    1 1 3 1
    2
    3 2333 6666
    2
    3 2333 6666
    2
    3 2333 6666

    output

    13002
    58489497
    12043005

    中文题意:给定一个序列A,长度最长为100000,初始值为0,现在有三种操作:

    1.对区间[l,r]中所有的数都加上一个值。

    2.对整个序列求一次前缀和。

    3.询问[l,r]区间内所有a的和。

     现在对1,0,0,0求3次前缀和得到下图

     可以发现(1,1)对右下角的点的贡献是

    接下来我们定义一个变量now记录数组了进行了几次求数组前缀和就是题目的2号操作。

    对于操作1,在[l,r]区间内每个数增加w。

    相当于在上次进行2号操作前,在点 L 增加w,在点 R+1 减少w。

    例如:在3到5号位置增加1

    序号    1 2 3 4 5 6 7 8 9 

    now     0 0 1 1 1 0 0 0 0     求前缀和后

    now-1  0 0 1 0 0 -1 0 0 0   求前缀和前

    对于询问的话只要求下次求完前缀和 (位置 R 的值) - (位置 L-1 的值)

    对于进行前缀和操作只要将now++即可

    具体操作看代码

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 typedef long long ll;
     6 const int maxn = 410000,mod=998244353;
     7 struct Stack
     8 {
     9     ll lie,time,value;
    10 } st[maxn];
    11 ll fac[maxn+10],ifac[maxn+10],top;
    12 
    13 ll quick_pow(ll a,ll b)
    14 {
    15     ll ans=1;
    16     while(b)
    17     {
    18         if(b&1)
    19             ans=1ll*ans*a%mod;
    20         a=1ll*a*a%mod;
    21         b>>=1;
    22     }
    23     return ans;
    24 }
    25 
    26 void init()
    27 {
    28     fac[0]=1;
    29     for(int i=1; i<=maxn; i++)
    30         fac[i]=1ll*fac[i-1]*i%mod;
    31     ifac[maxn]=quick_pow(fac[maxn],mod-2);
    32     for(int i=maxn-1; i>=0; i--)
    33         ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
    34 }
    35 
    36 ll C(ll n,ll m)
    37 {
    38     return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
    39 }
    40 
    41 inline ll solve(ll x,ll now)
    42 {
    43     if(x==0)return 0;
    44 
    45     ll sum = 0;
    46     for(int i=0; i<top; i++) ///计算每个更新对点的贡献值
    47     {
    48 
    49         if(st[i].lie>x)continue;
    50         ll lie = st[i].lie;
    51         ll per = st[i].time;
    52         ll value = st[i].value;
    53 
    54         ll aa = x-lie + now-per -1;
    55         ll bb = now-per -1;
    56 
    57         sum = (sum + value*C(aa,bb)%mod)%mod;
    58     }
    59     return sum;
    60 }
    61 
    62 int main()
    63 {
    64     init();  ///预处理阶乘和逆元将计算组合数的时间复杂度降为O(1)
    65     ll t,n,m,op;
    66     scanf("%lld",&t);
    67     while(t--)
    68     {
    69         scanf("%lld%lld",&n,&m);
    70         ll now = 1,l,r,value;
    71         top = 0;
    72         while(m--)
    73         {
    74             scanf("%lld",&op);
    75             if(op==1)
    76             {
    77                 scanf("%lld%lld%lld",&l,&r,&value);
    78                 st[top].value = value%mod,st[top].time=now-1,st[top].lie=l;
    79                 top++;
    80                 st[top].value =(mod-value)%mod,st[top].time=now-1,st[top].lie=r+1;
    81                 top++;
    82                 ///将更新存入数组
    83             }
    84             else if(op==2)
    85             {
    86                 now++;
    87             }
    88             else
    89             {
    90                 scanf("%lld%lld",&l,&r);
    91                 ll ans = solve(r,now+1)-solve(l-1,now+1);
    92                 ans = (ans+mod)%mod;
    93                 printf("%lld
    ",ans);
    94             }
    95         }
    96     }
    97     return 0;
    98 }
    View Code
  • 相关阅读:
    利用FlashPaper实现类似百度文库功能
    浅谈Oracle函数返回Table集合
    Oracle 触发器在日志管理开发中的应用
    Putty 工具 保存配置的 小技巧
    java.util.Date转java.sql.Date丢失时间问题
    java String和Date转换
    springboot项目使用拦截器修改/添加前端传输到后台header和cookie参数
    Spring的使用及Spring3.2控制器增强@ControllerAdvice
    使用fastjson统一序列化响应格式
    【转】Elastic-Job
  • 原文地址:https://www.cnblogs.com/xcantaloupe/p/9511909.html
Copyright © 2011-2022 走看看