[CF981E] Addition on Segments

Description

给定长度为 (n) 的序列,有 (q) 条操作,每条操作为将区间 ([l,r]) 加上 (x)。序列初始都为 (0)。问有多少个 $ k in [1,n]$ 满足能从 (q) 条操作中选出若干条操作后使得序列的最大值为 (k)

Solution

如果能有方案使得某个位置的值变成 (k),就一定存在方案使得最大值变为 (k)

考虑一个暴力,对于每个位置分别考虑是否能使它变为 (k),设 (f[i][j]) 表示前 (i) 条操作中选取若干操作是否能使得该位置的和变为 (j),用 Bitset 优化,总时间复杂度 (O(frac 1 w n^2 q))

考虑线段树分治,将 (q) 条操作中的每个区间拆分挂在线段树的若干结点上,然后将线段树 DFS 一遍,过程中利用结点上挂的区间来修改,历史记录用栈存储,这样回溯时可以撤销,到达叶子结点时即可得到当结点的答案。每次修改的复杂度为 (O(frac 1 w n)),最多有 (O(q log n)) 个拆分后的区间,故总时间复杂度为 (O(frac 1 w nq log n))

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

#define int long long
#define bst bitset<10005>
const int N = 1000005;

int n,q,l[N],r[N],x[N];
vector <int> tg[N]; // Tags on segment tree nodes
stack <bst> sta;
bst ans;

void modify(int p,int l,int r,int ql,int qr,int key)
{
    if(l>qr || r<ql) return;
    if(l>=ql&&r<=qr)
    {
        tg[p].push_back(key);
    }
    else
    {
        modify(p*2,l,(l+r)/2,ql,qr,key);
        modify(p*2+1,(l+r)/2+1,r,ql,qr,key);
    }
}

void solve(int p,int l,int r)
{
    bst now=sta.top();
    for(int i:tg[p])
    {
        now|=now<<i;
    }
    if(l==r)
    {
        ans|=now;
    }
    else
    {
        sta.push(now);
        solve(p*2,l,(l+r)/2);
        solve(p*2+1,(l+r)/2+1,r);
        sta.pop();
    }
}

signed main()
{
    ios::sync_with_stdio(false);

    cin>>n>>q;
    for(int i=1;i<=q;i++)
    {
        cin>>l[i]>>r[i]>>x[i];
        modify(1,1,n,l[i],r[i],x[i]);
    }

    bst b0;
    b0[0]=1;
    sta.push(b0);

    solve(1,1,n);

    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(ans[i]) ++cnt;
    }
    cout<<cnt<<endl;
    for(int i=1;i<=n;i++)
    {
        if(ans[i]) cout<<i<<" ";
    }
}

原文地址:https://www.cnblogs.com/mollnn/p/13631415.html