P4247 [清华集训 2012] 序列操作 Solution

Description

给定序列 a=(a1,a2,⋯ ,an)a=(a_1,a_2,\cdots,a_n)a=(a1,a2,,an),有 mmm 个操作分三种:

  • add⁡(l,r,v)\operatorname{add}(l,r,v)add(l,r,v):对每个 i∈[l,r]i\in [l,r]i[l,r] 执行 ai←ai+va_i\gets a_i+vaiai+v.
  • rev⁡(l,r)\operatorname{rev}(l,r)rev(l,r):对每个 i∈[l,r]i\in [l,r]i[l,r] 执行 ai←−aia_i\gets -a_iaiai.
  • query⁡(l,r,c)\operatorname{query}(l,r,c)query(l,r,c):求 (∑b⊆al⋯r,∣b∣=c∏i∈bi) mod 19940417\sum\limits_{b \subseteq a_{l\cdots r},|b|=c} \prod\limits_{i \in b}i) \bmod 19940417balr,b=cibi)mod19940417.

Limitations

1≤n,q≤5×1041 \le n,q \le 5\times 10^41n,q5×104
1≤l≤r≤n1 \le l \le r \le n1lrn
∣ai∣,∣v∣≤109|a_i|,|v| \le 10^9ai,v109
1≤c≤min⁡(r−l+1,20)1 \le c \le \min(r-l+1,20)1cmin(rl+1,20)
6s,250MB6\text{s},250\text{MB}6s,250MB

Solution

虽然 n,qn,qn,q 没到 10510^5105 级别,但暴力维护的复杂度是指数级的.
注意到 ccc 很小,因此复杂度可以带个 ccc,想到 DP.
dpl,r,c=(∑b⊆al⋯r,∣b∣=c∏i∈bi)\textit{dp}_{l,r,c}=(\sum\limits_{b \subseteq a_{l\cdots r},|b|=c} \prod\limits_{i \in b}i)dpl,r,c=(balr,b=cibi).
则可得 dpl,r,c=∑t=0ivl,j,t×dpj+1,r,c−t\textit{dp}_{l,r,c}=\sum\limits_{t=0}^i v_{l,j,t} \times \textit{dp}_{j+1,r,c-t}dpl,r,c=t=0ivl,j,t×dpj+1,r,ct,其中 l<j<rl<j<rl<j<r.
显然 dp\textit{dp}dp 可以在线段树 pushup 时维护。
接下来考虑修改:

  • 对于 rev⁡\operatorname{rev}rev:可以发现,只有 ccc 为奇数才要变号。
  • 对于 add⁡\operatorname{add}add:考虑把式子展开,发现是一个关于 vvvccc 次多项式,由二项式定理得 ndpl,r,c=∑i=0k((n−i+kk)×dpl,r,c−k×vk)\textit{ndp}_{l,r,c}=\sum\limits_{i=0}^k(\binom{n-i+k}{k} \times \textit{dp}_{l,r,c-k}\times v^k)ndpl,r,c=i=0k((kni+k)×dpl,r,ck×vk).

然后要注意 pushdown 时的顺序。
另外 19970417∉P19970417 \notin \mathbb{P}19970417/P,需要用杨辉三角,而不是费马小定理来求组合数.
可得时间复杂度 O(c2nlog⁡n)O(c^2n\log n)O(c2nlogn),空间复杂度 O(cnlog⁡n)O(cn\log n)O(cnlogn).

Code

8.92KB,12.93s,29.68MB  (in total, C++ 20 with O2)8.92\text{KB},12.93\text{s},29.68\text{MB}\;\texttt{(in total, C++ 20 with O2)}8.92KB,12.93s,29.68MB(in total, C++ 20 with O2)
线段树,modint 删了.

// Problem: P4247 [清华集训2012] 序列操作
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4247
// Memory Limit: 250 MB
// Time Limit: 6000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

inline int read() {
    int x = 0,f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

void write(int x) {
    if (x < 0) {
        putchar('-');
        x = -x;
    }
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
    return;
}

template <int MOD>
struct modint {};

template<class Info, class Tag>
struct lazy_segment{};

const int D = 20;
using Z = modint<19940417>;
array<Z, D + 1> power;
vector<array<Z, D + 1>> C;

struct Tag {
    Z add;
    bool rev;
    
    Tag(Z _add = 0, bool _rev = false): add(_add), rev(_rev) {}
    
    void apply(const Tag& t) {
        if (t.rev) {
            add = -add;
            rev ^= 1;
        }
        if (t.add != 0) {
            add += t.add;
        }
    }
};

struct Info {
    int len;
    array<Z, D + 1> c;
    
    Info(Z _val = 0, int _len = 1): len(_len) {
        c[0] = 1;
        c[1] = _val;
    }
    
    void apply(const Tag& t) {
        int size = min(len, D);
        if (t.rev) {
            for (int i = 1; i <= size; i += 2) {
                c[i] = -c[i];
            }
        }
        
        if (t.add != 0) {
            power[0] = 1;
            for (int i = 1; i <= size; i++) {
                power[i] = power[i - 1] * t.add;
            }
            
            for (int i = size; i; i--) {
                for (int j = 0; j < i; j++) {
                    c[i] += c[j] * power[i - j] * C[len - j][i - j];
                }
            }
        }
    }
};

Info operator+(const Info& lhs, const Info& rhs) {
    Info res;
    res.c.fill(0);
    res.len = lhs.len + rhs.len;
    
    int size = min(lhs.len, D), rlen = rhs.len;
    for (int i = 0; i <= size; i++) {
        for (int j = 0; j + i <= D && j <= rlen; j++) {
            res.c[i + j] += lhs.c[i] * rhs.c[j];
        }
    }
    return res;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	int n = read(), m = read();
	
	C.resize(n + 1);
	C[0][0] = 1;
	for (int i = 1; i <= n; i++) {
	    int siz = min(D, i);
	    C[i][0] = 1;
	    for (int j = 1; j <= siz; j++) {
	        C[i][j] = C[i - 1][j] + C[i - 1][j - 1];
	    }
	}
	
	vector<Info> a;
	for (int i = 0, v; i < n; i++) {
	    v = read();
	    a.emplace_back(Z(v), 1);
	}
	
	lazy_segment<Info, Tag> tree(a);
	for (int i = 0; i < m; i++) {
	    char op = getchar();
	    while (op != 'I' && op != 'R' && op != 'Q') {
	        op = getchar();
	    }
	    
	    int l = read(), r = read(), v;
	    l--, r--;
	    
	    if (op == 'I') {
	        v = read();
	        tree.apply(l, r, Tag(v, false));
	    }
	    if (op == 'R') {
	        tree.apply(l, r, Tag(0, true));
	    }
	    if (op == 'Q') {
	        v = read();
	        write(tree.query(l, r).c[v].val);
	        printf("\n");
	    }
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值