USACO 2018 January Contest Platinum A: Lifeguards 題解
阿新 • • 發佈:2019-02-08
將所有的區間按左端點從小到大排序
我們處理那些被完全包含的區間,這些區間即使刪除也不會使答案變壞
這樣先刪一波,如果發現這種小區間的個數多於k,就可以直接算答案了
否則我們要dp
設dp[i][j]為考慮到第i個區間,已經刪除了j個區間,且第i個區間沒有被刪除的情況下最大的覆蓋長度
顯然有狀態轉移方程dp[i][j]=max(dp[k][j-i-k-1]+第i個區間貢獻的覆蓋)
這個方程相當於列舉上一個沒有被刪除的區間k,然後將k+1~i-1全部刪除
但我們看到這個轉移是O(n)的,所以總複雜度為O(n*n*k),不能接受
考慮優化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值大的話,就把它從隊尾彈出
這樣總複雜度O(n*k)
#include <cstdio> #include <iostream> #include <cstring> #include <string> #include <cstdlib> #include <utility> #include <cctype> #include <algorithm> #include <bitset> #include <set> #include <map> #include <vector> #include <queue> #include <deque> #include <stack> #include <cmath> #define LL long long #define LB long double #define x first #define y second #define Pair pair<int,int> #define pb push_back #define pf push_front #define mp make_pair #define LOWBIT(x) x & (-x) using namespace std; const int MOD=1e9+7; const LL LINF=2e16; const int INF=2e9; const int magic=348; const double eps=1e-10; inline int getint() { char ch;int res;bool f; while (!isdigit(ch=getchar()) && ch!='-') {} if (ch=='-') f=false,res=0; else f=true,res=ch-'0'; while (isdigit(ch=getchar())) res=res*10+ch-'0'; return f?res:-res; } int n,k; int maxn[100048]; deque<Pair> q[100048]; int dp[100048][101]; Pair a[100048];bool exist[100048]; Pair x[100048];int x_top=0; struct node { int val; int nv; int from; bool type; }b[200048];int tot=0; Pair Pos[200048]; vector<int> fake; int c[200048]; inline bool update(int x,int delta) { while (x<=n*2) { c[x]+=delta; x+=LOWBIT(x); } } inline int query(int x) { int res=0; while (x) { res+=c[x]; x-=LOWBIT(x); } return res; } bool cmp_y(Pair x,Pair y) { return x.y<y.y; } bool cmp(node x,node y) { return x.val<y.val; } int main () { freopen ("lifeguards.in","r",stdin); freopen ("lifeguards.out","w",stdout); int i,j; n=getint();k=getint(); for (i=1;i<=n;i++) a[i].x=getint(),a[i].y=getint(); if (k>=n) { printf("0\n"); return 0; } sort(a+1,a+n+1); for (i=1;i<=n;i++) { b[++tot].val=a[i].x;b[tot].from=i;b[tot].type=false; b[++tot].val=a[i].y;b[tot].from=i;b[tot].type=true; } sort(b+1,b+tot+1,cmp); for (i=1;i<=tot;i++) if (!b[i].type) Pos[b[i].from].x=i; else Pos[b[i].from].y=i; for (i=1;i<=tot;i++) if (!b[i].type) update(i,1); else { update(Pos[b[i].from].x,-1); if (query(Pos[b[i].from].x)) fake.pb(b[i].from); } if (int(fake.size())>=k) { int ans=0,max_right=0; for (i=1;i<=n;i++) { if (max_right<=a[i].x) { ans+=a[i].y-a[i].x; } else if (max_right>=a[i].y) { continue; } else { ans+=a[i].y-max_right; } max_right=max(max_right,a[i].y); } printf("%d\n",ans); return 0; } k-=int(fake.size()); memset(exist,true,sizeof(exist)); for (i=0;i<int(fake.size());i++) exist[fake[i]]=false; for (i=1;i<=n;i++) if (exist[i]) x[++x_top]=a[i]; for (i=1;i<=x_top;i++) for (j=0;j<=k;j++) dp[i][j]=0; dp[1][0]=x[1].y-x[1].x; q[1].pb(mp(dp[1][0],x[1].y)); memset(maxn,0,sizeof(maxn)); for (i=2;i<=x_top;i++) for (j=0;j<=min(k,i-1);j++) { int ind=i-1-j; while (!q[ind].empty() && q[ind].front().y<=x[i].x) { maxn[ind]=max(maxn[ind],q[ind].front().x); q[ind].pop_front(); } dp[i][j]=max(dp[i][j],maxn[ind]+x[i].y-x[i].x); if (!q[ind].empty()) dp[i][j]=max(dp[i][j],q[ind].front().x+x[i].y-q[ind].front().y); int ins_ind=i-j; while (!q[ins_ind].empty() && q[ins_ind].back().x+x[i].y-q[ins_ind].back().y<=dp[i][j]) q[ins_ind].pop_back(); q[ins_ind].pb(mp(dp[i][j],x[i].y)); } int ans=0; for (i=1;i<=x_top;i++) for (j=0;j<=min(k,i-1);j++) if (j+x_top-i==k) ans=max(ans,dp[i][j]); printf("%d\n",ans); return 0; }