傳送門:戳我node
這道題有兩個版本,S和P,S是K等於1的狀況,顯然能夠用線段樹水過。ios
P版本就難了不少,洛谷黑題(NOI/NOI+/CTSC),嘿嘿。ide
我本身也不是很理解,照着題解寫了一遍,而後悟到了一點東西。優化
dp方程很好想:spa
dp[i][j]表示處理到第i個元素,已經刪掉了j個,但取了第i個。code
dp[i][j]=max(dp[k][j-i-k-1])。表示考慮上一個取的區間是k,而後刪去的個數就是j-i-k-1。blog
時間複雜度是O(N*K*K)。顯然不知足題目數據(或許卡卡常能過?)隊列
那麼考慮單調隊列優化get
由於我本身也有點蒙圈,怕誤導你們,就摘抄了其餘巨佬的博客博客
/* 這段內容摘自luogu用戶babingbaboom的博客:https://www.luogu.org/blog/user51357/solution-p4182
考慮優化dp轉移
對於第i個區間,設其左端點爲l
咱們先看一看方程,會發現對dp[i][j]產生貢獻的i'-j'=i-1-j
-
對於i以前的那些右端點<=l的區間,它們與i沒有重疊部分,因此只要在它們當中取max,再加上第i個區間的長度便可
-
對於那些與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); }