1. 程式人生 > >[USACO18JAN]Lifeguards P 洛谷黑題,單調佇列優化DP

[USACO18JAN]Lifeguards P 洛谷黑題,單調佇列優化DP

傳送門:戳我

這道題有兩個版本,S和P,S是K等於1的情況,顯然可以用線段樹水過。

P版本就難了很多,洛谷黑題(NOI/NOI+/CTSC),嘿嘿。

我自己也不是很理解,照著題解寫了一遍,然後悟到了一點東西。

dp方程很好想:

dp[i][j]表示處理到第i個元素,已經刪掉了j個,但取了第i個。

dp[i][j]=max(dp[k][j-i-k-1])。表示考慮上一個取的區間是k,然後刪去的個數就是j-i-k-1。

時間複雜度是O(N*K*K)。顯然不滿足題目資料(或許卡卡常能過?)

那麼考慮單調佇列優化

因為我自己也有點蒙圈,怕誤導大家,就摘抄了其他巨佬的部落格

/*  這段內容摘自luogu使用者babingbaboom的部落格:https://www.luogu.org/blog/user51357/solution-p4182

考慮優化dp轉移

對於第i個區間,設其左端點為l

我們先看一看方程,會發現對dp[i][j]產生貢獻的i'-j'=i-1-j

  1. 對於i之前的那些右端點<=l的區間,它們與i沒有重疊部分,所以只要在它們當中取max,再加上第i個區間的長度即可

  2. 對於那些與i有重疊部分的區間,在當前區間右移的時候,這些dp的貢獻會變,但相對大小不會變,所以可以維護一個單調佇列,dp[i][j]對應的單調佇列的編號為i-1-j,每次先把隊頭的那些已經跑到左邊的區間彈出去(算成1的貢獻),然後取隊頭就是當前的有重疊的狀態中的最大答案

然後當前dp值算出來以後要插進對應的單調佇列中(編號為i-j的單調佇列),如果隊尾狀態加上與當前狀態的右端點差還沒有當前狀態的dp值大的話,就把它從隊尾彈出

*/

程式碼實現如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxn 100001
using namespace std;
inline void read(int &x)
{
    x=0;int f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1
;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} x*=f; } int N,K,dp[maxn][110]; int p[maxn]; struct coww{ int l,r; friend bool operator < (coww a,coww b) { if(a.l==b.l) return a.r>b.r; else return a.l<b.l; } }arr[maxn],cow[maxn]; struct node{ int node,val; }; deque<node>que[maxn]; int main() { read(N);read(K); if (K>=N) { printf("0"); return 0; } for(int i=1;i<=N;i++) { read(arr[i].l); read(arr[i].r); } sort(arr+1,arr+1+N); int right=0,cnt=0; cow[0]=(coww){0,0}; for(int i=1;i<=N;i++) { if(arr[i].r>right) cow[++cnt]=arr[i],right=arr[i].r; else K--; } if(K<0) K=0; N=cnt; for(int i=1;i<=N;i++) { int u=min(K+1,i); for(int j=0;j<u;j++) { int now=i-j-1; while(!que[now].empty()&&cow[que[now].front().node].r<cow[i].l) { node to=que[now].front(); p[now]=max(p[now],to.val+cow[to.node].r); que[now].pop_front(); } dp[i][j]=max(dp[i][j],p[now]+cow[i].r-cow[i].l); if (!que[now].empty()) dp[i][j]=max(dp[i][j],que[now].front().val+cow[i].r); int nowv=dp[i][j]-cow[i].r; now=i-j; while ((!que[now].empty())&&(que[now].back().val<nowv)) que[now].pop_back(); que[now].push_back((node){i,nowv}); } } int ans=0; for (int i=1;i<=N;i++) for (int j=0;j<min(i,K+1);j++) if (j+N-i==K) ans=max(ans,dp[i][j]); printf("%d",ans); }
View Code