[CF981E] Addition on Segments - 線段樹分治,bitset
阿新 • • 發佈:2020-09-08
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<<" "; } }