[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<<" ";
}
}