将所有的区间按左端点从小到大排序
我们处理那些被完全包含的区间,这些区间即使删除也不会使答案变坏
这样先删一波,如果发现这种小区间的个数多于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;
}