「BZOJ2962」序列操作-线段树

本文介绍了一种处理区间操作和复杂查询的算法解决方案。通过动态规划和线段树结合的方法,实现了对序列进行区间加法、区间数值反转及求特定数量元素乘积之和的高效计算。

Decription

有一个长度为nn的序列和q个操作。

有三种操作

  1. II a bb c表示将[a,b][a,b]这一段区间的元素集体增加cc

  2. R aa b表示将[a,b][a,b]区间内所有元素变成相反数。

  3. QQ a bb c表示询问[a,b][a,b]这一段区间中选择cc个数相乘的所有方案的和mod19940417的值。

    n,q50000c20n,q⩽50000,c⩽20

Solution

DPDP数组fifi表示在当前区间选择ii个数相乘的所有方案的和。每次合并时卷积即可。

对于2操作,将奇数项的f值取相反数即可。

而对于11操作,维护x的系数,每次添加时计算即可。具体来说

sumi=j=1ixj×sumij×C(szi+j,j)+sumisumi=∑j=1ixj×sumi−j×C(sz−i+j,j)+sumi
#include <bits/stdc++.h>
using namespace std;

typedef long long lint;
const int mod = 19940417;
const int maxn = 50005;

int n, q, C[maxn][21];

inline int gi()
{
    char c = getchar(); bool f = 1;
    while ((c < '0' || c > '9') && c != '-') c = getchar();
    if (c == '-') f = 0, c = getchar();
    int sum = 0;
    while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
    return f ? sum : mod - sum;
}

#define lch (s << 1)
#define rch (s << 1 | 1)
#define mid ((l + r) >> 1)
#define Mid (mid + 1)

int f[maxn * 6][21], lazy1[maxn * 6], lazy2[maxn * 6];

inline void merge(int s)
{
    for (int i = 1; i <= 20; ++i) {
        f[s][i] = 0;
        for (int j = 0; j <= i; ++j) {
            f[s][i] += (lint)f[lch][j] * f[rch][i - j] % mod;
            if (f[s][i] >= mod) f[s][i] -= mod;
        }
    }
}

int tmp[21];
void increase(int s, int v, int sz)
{
    for (int i = min(20, sz); i >= 1; --i) {
        tmp[i] = 0;
        for (int j = 1, powv = v; j <= i; ++j, powv = (lint)powv * v % mod) {
            tmp[i] += (lint)powv * f[s][i - j] % mod * C[sz - i + j][j] % mod;
            if (tmp[i] >= mod) tmp[i] -= mod;
        }
    }
    for (int i = min(20, sz); i >= 1; --i) {
        f[s][i] += tmp[i];
        if (f[s][i] >= mod) f[s][i] -= mod;
    }
    lazy2[s] += v; if (lazy2[s] >= mod) lazy2[s] -= mod;
}

inline void pushdown(int s, int l, int r)
{
    if (lazy1[s]) {
        for (int i = 1; i <= 20; i += 2) {
            if (f[lch][i]) f[lch][i] = mod - f[lch][i];
            if (f[rch][i]) f[rch][i] = mod - f[rch][i];
        }
        lazy1[lch] ^= 1; if (lazy2[lch]) lazy2[lch] = mod - lazy2[lch];
        lazy1[rch] ^= 1; if (lazy2[rch]) lazy2[rch] = mod - lazy2[rch];
        lazy1[s] = 0;
    }
    if (lazy2[s]) {
        increase(lch, lazy2[s], mid - l + 1);
        increase(rch, lazy2[s], r - Mid + 1);
        lazy2[s] = 0;
    }
}

void build(int s, int l, int r)
{
    f[s][0] = 1;
    if (l == r) {
        f[s][1] = gi() % mod;
        return;
    }
    build(lch, l, mid);
    build(rch, Mid, r);
    merge(s);
}

void insert(int s, int l, int r, int x, int y, int v)
{
    if (x <= l && r <= y) {
        increase(s, v, r - l + 1);
        return ;
    }
    pushdown(s, l, r);
    if (x <= mid) insert(lch, l, mid, x, y, v);
    if (Mid <= y) insert(rch, Mid, r, x, y, v);
    merge(s);
}

void reverse(int s, int l, int r, int x, int y)
{
    if (x <= l && r <= y) {
        for (int i = 1; i <= 20; i += 2) if (f[s][i]) f[s][i] = mod - f[s][i];
        lazy1[s] ^= 1; if(lazy2[s]) lazy2[s] = mod - lazy2[s];
        return;
    }
    pushdown(s, l, r);
    if (x <= mid) reverse(lch, l, mid, x, y);
    if (Mid <= y) reverse(rch, Mid, r, x, y);
    merge(s);
}

int ans[21];
void query(int s, int l, int r, int x, int y)
{
    if (x <= l && r <= y) {
        for (int i = 1; i <= 20; ++i) {
            tmp[i] = 0;
            for (int j = 0; j <= i; ++j) {
                tmp[i] += (lint)ans[j] * f[s][i - j] % mod;
                if (tmp[i] >= mod) tmp[i] -= mod;
            }
        }
        for (int i = 1; i <= 20; ++i) ans[i] = tmp[i];
        return ; 
    }
    pushdown(s, l, r);
    if (x <= mid) query(lch, l, mid, x, y);
    if (Mid <= y) query(rch, Mid, r, x, y);
}

int main()
{
    freopen("sequence.in", "r", stdin);
    freopen("sequence.out", "w", stdout);

    n = gi(); q = gi();

    C[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        C[i][0] = 1;
        for (int j = 1; j <= 20; ++j) {
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
            if (C[i][j] >= mod) C[i][j] -= mod;
        }
    }

    build(1, 1, n);

    char c;
    for (int l, r, i = 1; i <= q; ++i) {
        c = getchar();
        while (c < 'A' || c > 'Z') c = getchar();
        if (c == 'I') l = gi(), r = gi(), insert(1, 1, n, l, r, gi());
        else if (c == 'R') l = gi(), r = gi(), reverse(1, 1, n, l, r);
        else {
            for (int i = 1; i <= 20; ++i) ans[i] = 0;
            ans[0] = 1;
            l = gi(); r = gi(); query(1, 1, n, l, r);
            printf("%d\n", ans[gi()]);
        }
    }

    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值