思路:
先將時間進行離散化,設總時間為 \(cnt\),然後考慮求出 \(W(l,r)\),即在時間段 \([l,r]\) 內的所有節目,可以 \(n^2\) 字首和,也可以 \(n^3\) 暴力。
然後定義 \(f_{i,j}\) 表示前 \(i\) 個時間,一號場地有 \(j\) 個節目時,二號場地最多的節目數量,則狀態轉移方程為:
那麼可以得到:
則第一個問的時間複雜度為 \(O(N^3)\)。
然後再看第二個問,需要定義 \(g_{i,j}\) 表示 \(i \sim cnt\) 的時間段,一號場地有 \(j\) 個節目時,二號場地最多的節目數量,則狀態轉移方程類似:
然後再定義 \(dp_{l,r}\) 表示若強制選 \([l,r]\) 的全部節目的區域性最優解,則可以列舉左邊和右邊一號場有多少個進行轉移:
那麼此時若強制選 \([l,r]\) 的答案,是 \(dp_{l,r}\) 嗎?答案很明顯,不是。
因為 \(f_{l-1,i}\) 和 \(g_{r+1,j}\) 只保證了 \(1 \sim l-1,r+1 \sim j\) 的區域性最優,即可能會有一些活動的一端在 \([l,r]\) 內,但是另一端不在 \([l,r]\) 內,此時選這些節目可能會更優。
於是我們需要列舉一個 \([L,R]\),使得 \([L,R]\) 包含 \([l,r]\),故 \([l,r]\) 強制選的答案為:
故只要我們求出 \(dp\),就可以 \(O(N^3)\) 的得到答案。
但是計算 \(dp\) 的時間複雜度為 \(O(N^4)\),且常數較大,需要最佳化卡常大師應該能衝過去。
注意到 \(f_{l,i}\) 和 \(g_{r,j}\) 是會隨著 \(i/j\) 的增大而不增的。
此時若 \(i\) 不動,則對於 \(j\) 來說, \(i+W(l,r)+j\) 是單增的,\(f_{l,i} + g_{r,j}\) 是單降的,故 \(H(y)=\min(i+W(l,r)+j,f_{l,i} + g_{r,j})\) 是一個單峰的函式,我們需要找到其最大值。
故我們可以對 \(j\) 進行走指標,從 \(yjn\) 開始,若 \(H(j-1)\ge H(j)\),就可以令 \(j \gets j - 1\)。
這裡需要證明一下在 \(i\) 增加時,\(j\) 的單峰函式只會向左平移,因為這樣才可以走指標:
在 \(i\) 增加時,\(i+W(l,r)+j\) 也會增加。
而對於 \(f_{l,i}+g_{r,j}\) 不增。
故會峰點左移。
總時間複雜度是 \(O(N^3)\)。
完整程式碼:
#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const int N=205,M=410,INF=1e9;
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*f;
}
inline void write(ll x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9)
write(x/10);
putchar(x%10+'0');
}
struct Seg{
int l,r;
int id;
}A[N];
int n,cnt,Max;
int ans[N],s[N],h[M];
int F[M][N],G[M][N],dp[M][M],W[M][M];
int get(int i,int j,int l,int r){
return min(i+W[l][r]+j,F[l][i]+G[r][j]);
}
bool End;
int main(){
// open("A.in","A.out");
n=read();
for(int i=1;i<=n;i++){
A[i].l=read(),A[i].r=A[i].l+read();
A[i].id=i;
h[++cnt]=A[i].l;
h[++cnt]=A[i].r;
}
sort(h+1,h+cnt+1);
cnt=unique(h+1,h+cnt+1)-(h+1);
for(int i=1;i<=n;i++){
A[i].l=lower_bound(h+1,h+cnt+1,A[i].l)-h;
A[i].r=lower_bound(h+1,h+cnt+1,A[i].r)-h;
}
for(int l=1;l<=cnt;l++)
for(int r=l;r<=cnt;r++)
for(int i=1;i<=n;i++)
if(l<=A[i].l&&A[i].r<=r)
W[l][r]++;
for(int i=0;i<=cnt;i++)
for(int j=0;j<=n;j++)
F[i][j]=-INF;
F[0][0]=0;
for(int i=1;i<=cnt;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<i;k++)
F[i][j]=max({F[i][j],F[k][j]+W[k][i],(j>=W[k+1][i])?(F[k][j-W[k][i]]):(-INF)});
for(int i=1;i<=cnt+1;i++)
for(int j=0;j<=n;j++)
G[i][j]=-INF;
G[cnt+1][0]=0;
for(int i=cnt;i>=1;i--)
for(int j=0;j<=n;j++)
for(int k=i+1;k<=cnt+1;k++)
G[i][j]=max({G[i][j],G[k][j]+W[i][k],(j>=W[i+1][k])?(G[k][j-W[i][k]]):(-INF)});
for(int l=1;l<=cnt;l++){
for(int r=l;r<=cnt;r++){
for(int x=0,y=n;x<=n;x++){
while(y&&get(x,y-1,l,r)>=get(x,y,l,r))
y--;
dp[l][r]=max(dp[l][r],get(x,y,l,r));
}
}
}
for(int i=0;i<=n;i++)
ans[0]=max(ans[0],min(i,F[cnt][i]));
for(int i=1;i<=n;i++)
for(int L=1;L<=A[i].l;L++)
for(int R=A[i].r;R<=cnt;R++)
ans[i]=max(ans[i],dp[L][R]);
for(int i=0;i<=n;i++){
write(ans[i]);
putchar('\n');
}
cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
return 0;
}