1. 程式人生 > >USACO 2018 January Contest Platinum A: Lifeguards 題解

USACO 2018 January Contest Platinum A: Lifeguards 題解

將所有的區間按左端點從小到大排序

我們處理那些被完全包含的區間,這些區間即使刪除也不會使答案變壞

這樣先刪一波,如果發現這種小區間的個數多於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;
}