2024牛客暑期多校训练营9 B.Break Sequence【线段树优化DP】

Break Sequence

题意

给定一个长度为 n n n 的数组 a a a 和一个大小为 m m m 的集合 S S S
计算有多少种方式将 a a a 划分成若干个子段 [ 1 , p 1 ] , [ p 1 + 1 , p 2 ] , . . . . . , [ p k − 1 + 1 , n ] [1, p_1], [p_1 + 1, p_2], ....., [p_{k - 1} + 1, n] [1,p1],[p1+1,p2],.....,[pk1+1,n],满足每个子段内,每种数字的出现次数都不等于 S S S 中的元素

1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1n2×105
0 ≤ m ≤ 10 0 \leq m \leq 10 0m10
1 ≤ a i ≤ n 1 \leq a_i \leq n 1ain

思路

定义 d p i dp_i dpi [ 1 , i ] [1, i] [1,i] 的最后一个子段末尾在 i i i 的合法方案数,可以直接 O ( n 2 ) O(n^2) O(n2) 转移
由于 m m m 很小,我们进一步观察发现:在每个 i i i,对于当前的 a i a_i ai,其出现次数在 S S S 中的情况对应的左端点(前缀和)一定不超过 O ( ∣ S ∣ ) O(|S|) O(S) 个区间

于是我们可以每次扫到 a i a_i ai 的时候,在线更新这些非法区间,不允许从这些区间转移,注意如果当前 a i a_i ai 在前面已经出现过,我们要先消除前面打上的标记,因为我们当前考虑的是结尾在 i i i

区间标记我们可以用懒标线段树,最后区间查询 t a g tag tag 最少 min ⁡ \min min 0 0 0)的区间,将这些区间的 d p dp dp 值求和返回即可

线段树的信息包含两个: m n mn mn(最小值) 和 s u m sum sum(这个区间的最小 t a g tag tag m n mn mn d p dp dp 值之和)
叶子节点的 s u m sum sum 即为这个位置的 d p dp dp 值,向上合并左右儿子或查询的时候,比较一下两者的 m n mn mn 大小, s u m sum sum 值即为 m n mn mn 最小的值之和

时间复杂度: O ( n m log ⁡ n ) O(nm\log n) O(nmlogn)

#include<bits/stdc++.h>
#define fore(i,l,r)	for(int i=(int)(l);i<(int)(r);++i)
#define fi first
#define se second
#define endl '\n'
#define ull unsigned long long
#define ALL(v) v.begin(), v.end()
#define Debug(x, ed) std::cerr << #x << " = " << x << ed;

const int INF=0x3f3f3f3f;
const long long INFLL=1e18;

typedef long long ll;

template<class T>
constexpr T power(T a, ll b){
    T res = 1;
    while(b){
        if(b&1) res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

constexpr ll mul(ll a,ll b,ll mod){ //快速乘,避免两个long long相乘取模溢出
    ll res = a * b - ll(1.L * a * b / mod) * mod;
    res %= mod;
    if(res < 0) res += mod; //误差
    return res;
}

template<ll P>
struct MLL{
    ll x;
    constexpr MLL() = default;
    constexpr MLL(ll x) : x(norm(x % getMod())) {}

    static ll Mod;
    constexpr static ll getMod(){
       if(P > 0) return P;
       return Mod;
    }

    constexpr static void setMod(int _Mod){
       Mod = _Mod;
    }
    constexpr ll norm(ll x) const{
       if(x < 0){
           x += getMod();
       }
       if(x >= getMod()){
           x -= getMod();
       }
       return x;
    }
    constexpr ll val() const{
       return x;
    }
    explicit constexpr operator ll() const{ 
       return x; //将结构体显示转换为ll类型: ll res = static_cast<ll>(OBJ)
    }
    constexpr MLL operator -() const{ //负号,等价于加上Mod
       MLL res;
       res.x = norm(getMod() - x);
       return res;
    }
    constexpr MLL inv() const{
       assert(x != 0);
       return power(*this, getMod() - 2); //用费马小定理求逆
    }
    constexpr MLL& operator *= (MLL rhs) & { //& 表示“this”指针不能指向一个临时对象或const对象
       x = mul(x, rhs.x, getMod()); //该函数只能被一个左值调用
       return *this;
    }
    constexpr MLL& operator += (MLL rhs) & {
       x = norm(x + rhs.x);
       return *this;
    }
    constexpr MLL& operator -= (MLL rhs) & {
       x = norm(x - rhs.x);
       return *this;
    }
    constexpr MLL& operator /= (MLL rhs) & {
       return *this *= rhs.inv();
    }
    friend constexpr MLL operator * (MLL lhs, MLL rhs){
       MLL res = lhs;
       res *= rhs;
       return res;
    }
    friend constexpr MLL operator + (MLL lhs, MLL rhs){
       MLL res = lhs;
       res += rhs;
       return res;
    }
    friend constexpr MLL operator - (MLL lhs, MLL rhs){
       MLL res = lhs;
       res -= rhs;
       return res;
    }
    friend constexpr MLL operator / (MLL lhs, MLL rhs){
       MLL res = lhs;
       res /= rhs;
       return res;
    }
    friend constexpr std::istream& operator >> (std::istream& is, MLL& a){
       ll v;
       is >> v;
       a = MLL(v);
       return is;
    }
    friend constexpr std::ostream& operator << (std::ostream& os, MLL& a){
       return os << a.val();
    }
    friend constexpr bool operator == (MLL lhs, MLL rhs){
       return lhs.val() == rhs.val();
    }
    friend constexpr bool operator != (MLL lhs, MLL rhs){
       return lhs.val() != rhs.val();
    }
};

const ll mod = 998244353;
using Z = MLL<mod>;
template<>
ll MLL<0ll>::Mod = 998244353;

template<class Info, class Tag>
struct LazySegmentTree {
    const int n;
    std::vector<Info> info;
    std::vector<Tag> tag;
    LazySegmentTree(int n) : n(n), info(4 << std::__lg(n)), tag(4 << std::__lg(n)) {}
    LazySegmentTree(std::vector<Info> init) : LazySegmentTree(init.size()) {
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (l == r) {
                info[p].x = init[l - 1].x; //传入的数组0indexed
                info[p].len = 1;
                return;
            }
            int m = l + r >> 1;
            build(p << 1, l, m);
            build(p << 1 | 1, m + 1, r);
            pull(p);
        };
        build(1, 1, n);
    }
    void pull(int p) {
        info[p] = info[p << 1] + info[p << 1 | 1];
    }
    void apply(int p, const Tag &v) {
        info[p].apply(v);
        tag[p].apply(v);
    }
    void push(int p) {
        apply(p << 1, tag[p]);
        apply(p << 1 | 1, tag[p]);
        tag[p] = Tag();
    }
    Info query(int p, int l, int r, int ql, int qr) {
        if(qr < l || r < ql) return Info();
        if(ql <= l && r <= qr) return info[p];
        int m = l + r >> 1;
        push(p);
        return query(p << 1, l, m, ql, qr) + query(p << 1 | 1, m + 1, r, ql, qr);
    }
    void update(int p, int l, int r, int ql, int qr, const Tag &v) {
        if(qr < l || r < ql) return;
        if(ql <= l && r <= qr){
            apply(p, v);
            return;
        }
        int m = l + r >> 1;
        push(p);
        if(ql <= m) update(p << 1, l, m, ql, qr, v);
        if(qr > m) update(p << 1 | 1, m + 1, r, ql, qr, v);
        pull(p);
    }
    void update(int p, int l, int r, int qp, const Info &v) {
        if(l == r){
            info[p] = v;
            return;
        }
        int m = l + r >> 1;
        push(p);
        if(qp <= m) update(p << 1, l, m, qp, v);
        else update(p << 1 | 1, m + 1, r, qp, v);
        pull(p);
    }
};

struct Tag {
    int add = 0;
    Tag(int _add = 0) : add(_add) {}
    
    void apply(Tag t) {
        add += t.add;
    }
};

struct Info {
    int mn = 0;
    Z w = 0;
    Info(int _mn = 0, Z _w = 0) : mn(_mn), w(_w) {}
    
    void apply(Tag t) {
        mn += t.add;
    }
};

Info operator+(Info a, Info b) {
    Info c;
    c.mn = std::min(a.mn, b.mn);
    if(a.mn == b.mn) c.w = a.w + b.w;
    else if(a.mn < b.mn) c.w = a.w;
    else c.w = b.w;
    return c;
}


int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n + 1);
    std::vector<std::vector<int>> pos(n + 1);
    std::vector<int> s(m);
    fore(i, 1, n + 1) std::cin >> a[i];
    fore(i, 0, m) std::cin >> s[i];
    std::sort(ALL(s));

    LazySegmentTree<Info, Tag> seg(n);
    std::vector<Z> dp(n + 1 , 0);
    dp[0] = 1;
    const int D = 1;
    seg.update(1, 1, n, 0 + D, Info(0, 1));

    auto work = [&](int val, int delta){
        int sz = pos[val].size();
        for(auto k : s){
            if(k > sz) return;
            int l = (k == sz ? 0 : pos[val][sz - k - 1]), r = pos[val][sz - k] - 1;
            seg.update(1, 1, n, l + D, r + D, Tag(delta));
        }
    };

    fore(i, 1, n + 1){
        work(a[i], -1); //消除之前的tag
        pos[a[i]].push_back(i);
        work(a[i], 1);
        auto [mn, w] = seg.query(1, 1, n, 0 + D, i - 1 + D);
        if(!mn) dp[i] = w;
        if(i < n) seg.update(1, 1, n, i + D, Info(0, dp[i]));
    }

    std::cout << dp[n] << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值