https://codeforces.com/gym/104369
B. Base Station Construction
题意
现在需要在点 1,2,⋯,n 上建设基站,在点 i 建设基站的费用为 ai。有 m 个要求,每个要求为区间 [li,ri] 内至少有一个基站。问最小费用是多少。
题解
如果每个点的费用一样,那这是个经典的贪心问题。但是由于费用不同,显然无法贪心,考虑使用dp。
设 fi 表示前 i 个位置且第 i 个位置必须建设基站的最小总成本。考虑上一个建设基站的位置 j,有转移
fi=jminfj+ai
考虑这些限制,就是 [j+1,i−1] 不能包含一个要求的区间,所以,对于每个 i 我们可以预处理出最小的 pi,满足 [pi,i] 不包含一个要求的区间,这可以用双指针处理。
上面的dp可以利用单调队列优化。以前写的单调队列优化dp似乎总有这样那样的问题,这次抄了一份std,希望可以一直用。
还有一个小技巧是,为了统计最终答案,我们可以令 an+1=0,然后要求 [n+1,n+1] 必须有一个基站,这样答案就是 fn+1。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=5e5+5; int a[N],p[N],q[N]; ll f[N]; vector<int> b[N]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int _;cin>>_; while(_--) { int n;cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; a[++n]=0; for(int i=1;i<=n;i++) b[i].clear(); int m;cin>>m; while(m--) { int l,r;cin>>l>>r; b[l].push_back(-r),b[r].push_back(l); } b[n].push_back(-n);b[n].push_back(n); int cur=0; for(int i=1,j=1;j<=n;j++) { for(int x:b[j]) if(x>0&&x>=i) cur++; while(cur>0&&i<=j) { for(int x:b[i]) if(x<0&&-x<=j) cur--; i++; } p[j]=i; } f[0]=0,f[1]=a[1]; int h=1,t=1; q[t++]=0,q[t++]=1; for(int i=2;i<=n;i++) { int lim=p[i-1]-1; while(q[h]<lim) h++; f[i]=f[q[h]]+a[i]; while(h<t&&f[i]<=f[q[t-1]]) t--; q[t++]=i; } cout<<f[n]<<'\n'; } return 0; }
|