BZOJ 2962 序列操作 线段树

题目大意

给出一个序列,有三种操作,区间加,区间取负,询问一个区间内c个数字相乘的所有情况的和。

思路

利用线段树的简单应用,我们不难实现前两个操作。第三个操作需要推推公式。合并两个区间比较简单,计算当前区间取k个时,只需要枚举左边和右边分别取多少个然后乘起来就行了。区间取负把取奇数个数的答案取负,最复杂的是区间加。看代码吧,懒了。。

CODE

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 100010
#define MO 19940417
using namespace std;
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
#define CNT (r - l + 1)

int C[MAX][25];

inline void U(int &x,int y)
{
    x += y;
    if(x < 0)   x += MO;
    if(x >= MO) x -= MO;
}

struct Status{
    int ans[25];
    int plus;
    bool flag;

    Status() {
        memset(ans,0,sizeof(ans));
        plus = flag = 0;
    }
    Status operator +(const Status &a)const {
        Status re;
        re.ans[0] = 1;
        for(int i = 0; i <= 20; ++i)
            for(int j = 0; i + j <= 20; ++j) {
                if(i + j == 0)  continue;
                U(re.ans[i + j],(long long)ans[i] * a.ans[j] % MO);
            }
        return re;
    }
    void Plus(int _,int size) {
        U(plus,_);
        static int temp[25];
        for(int i = 1; i <= 20; ++i) {
            temp[i] = 0;
            int tx = 1;
            for(int j = i; ~j; --j) {
                U(temp[i],(long long)ans[j] * C[size - j][i - j] % MO * tx % MO);
                tx = (long long)tx * _ % MO;
            }
        }
        for(int i = 1; i <= 20; ++i)
            ans[i] = temp[i];
    }
    void Reverse() {
        flag ^= 1;
        for(int i = 1; i <= 19; i += 2)
            ans[i] = (MO - ans[i]) % MO;
        plus = (MO - plus) % MO;
    }
}tree[MAX << 2];

int src[MAX];

void BuildTree(int l,int r,int pos)
{
    tree[pos].ans[0] = 1;
    if(l == r) {
        tree[pos].ans[1] = src[l];
        return;
    }
    int mid = (l + r) >> 1;
    BuildTree(l,mid,LEFT);
    BuildTree(mid + 1,r,RIGHT);
    tree[pos] = tree[LEFT] + tree[RIGHT];
}

void Pretreatment(int cnt)
{
    for(int i = 0; i <= cnt; ++i)
        C[i][0] = 1;
    for(int i = 1; i <= cnt; ++i)
        for(int j = 1; j <= 20; ++j)
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MO;
}

inline void PushDown(int pos,int cnt)
{
    if(tree[pos].flag) {
        tree[LEFT].Reverse();
        tree[RIGHT].Reverse();
        tree[pos].flag = false;
    }
    if(tree[pos].plus) {
        tree[LEFT].Plus(tree[pos].plus,cnt - (cnt >> 1));
        tree[RIGHT].Plus(tree[pos].plus,cnt >> 1);
        tree[pos].plus = 0;
    }
}

void Modify(int l,int r,int x,int y,int c,int pos)
{
    if(l == x && y == r) {
        tree[pos].Plus(c,CNT);
        return;
    }
    PushDown(pos,CNT);
    int mid = (l + r) >> 1;
    if(y <= mid)    Modify(l,mid,x,y,c,LEFT);
    else if(x > mid)    Modify(mid + 1,r,x,y,c,RIGHT);
    else {
        Modify(l,mid,x,mid,c,LEFT);
        Modify(mid + 1,r,mid + 1,y,c,RIGHT);
    }
    tree[pos] = tree[LEFT] + tree[RIGHT];
}

void Reverse(int l,int r,int x,int y,int pos)
{
    if(l == x && y == r) {
        tree[pos].Reverse();
        return;
    }
    PushDown(pos,CNT);
    int mid = (l + r) >> 1;
    if(y <= mid)    Reverse(l,mid,x,y,LEFT);
    else if(x > mid)    Reverse(mid + 1,r,x,y,RIGHT);
    else {
        Reverse(l,mid,x,mid,LEFT);
        Reverse(mid + 1,r,mid + 1,y,RIGHT);
    }
    tree[pos] = tree[LEFT] + tree[RIGHT];
}

Status Ask(int l,int r,int x,int y,int pos)
{
    if(l == x && y == r)    return tree[pos];
    PushDown(pos,CNT);
    int mid = (l + r) >> 1;
    if(y <= mid)    return Ask(l,mid,x,y,LEFT);
    if(x > mid)     return Ask(mid + 1,r,x,y,RIGHT);
    Status left = Ask(l,mid,x,mid,LEFT);
    Status right = Ask(mid + 1,r,mid + 1,y,RIGHT);
    return left + right;
}

int cnt,asks;
char s[10];

int main()
{
    cin >> cnt >> asks;
    Pretreatment(cnt);
    for(int i = 1; i <= cnt; ++i)
        scanf("%d",&src[i]),src[i] = (src[i] % MO + MO) % MO;
    BuildTree(1,cnt,1);
    for(int x,y,z,i = 1; i <= asks; ++i) {
        scanf("%s",s);
        if(s[0] == 'I') {
            scanf("%d%d%d",&x,&y,&z);
            z = (z % MO + MO) % MO;
            Modify(1,cnt,x,y,z,1);
        }
        else if(s[0] == 'R') {
            scanf("%d%d",&x,&y);
            Reverse(1,cnt,x,y,1);
        }
        else {
            scanf("%d%d%d",&x,&y,&z);
            Status ans = Ask(1,cnt,x,y,1);
            printf("%d\n",ans.ans[z] % MO);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值