1. 程式人生 > 實用技巧 >luogu P1973 [NOI2011]NOI 嘉年華 dp

luogu P1973 [NOI2011]NOI 嘉年華 dp

LINK:NOI 嘉年華

一道質量非常高的dp題目。

考慮如何求出第一問 容易想到dp.

按照左端點排序/右端點排序狀態還是很難描述。

但是我們知道在時間上肯定是一次選一段 所以就可以直接利用時間點來dp.

直接將時間離散再做 因為有兩個元素使得最大的最小 二分是無意義的。

每次選擇是一段所以沒必要知道上次選到的時間點在哪 直接列舉上次決策。

分別記錄兩個地方的值是否存在也是可以優化的 可以只記錄一個然後第二個變成最多拿到多少即可。

這樣就有狀態\(f_{i,j}\)表示到了i這個時間點第一個的值為j的時候第二個的最大值。

轉移列舉k 然後預處理\(c_{i,j}\)表示這段時間的任務數即可。

複雜度\(n^3\)

考慮第二問。強制選擇某個點 在dp中是很難做到的 所以可以考慮強行將某個區間給第一個會場然後求出第二個會場的最大值。

強行給的話這個地方可以使用前後包夾做 即再做一個字尾dp g陣列。

那麼強行包含\(l,r\)的最大dp值就是 \(ans_{l,r}=\sum_{x}\sum_{y}max(x+y+c_{l,r},f_{l,x}+g_{r,y})\)

求出這個東西看似是\(n^4\)的 實際上隨著x的增大y是不增的。

證明這一點可以進行比較繁雜的分類討論 不過可以比較直觀的想出來。這裡不再贅述。

所以可以優化到\(n^3\).

考慮最後的答案 對於一個詢問l,r 答案不一定是\(l,r\)

因為此時是把l r單獨給抽出來了。

可能一些大的區間一起並且包含其可能是最優的 所以此時列舉包含這個區間的一些區間取max即可。

總複雜度\(n^3\).

code
//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define gc(a) scanf("%s",a+1)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-4
#define sq sqrt
#define S second
#define F first
#define mod 1000000007
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    RE int x=0,f=1;RE char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=410;
int n,m,cnt;
int b[MAXN];
int f[MAXN][MAXN],g[MAXN][MAXN],c[MAXN][MAXN],ans[MAXN][MAXN];
struct wy{int l,r;}t[MAXN];
int main()
{
	//freopen("1.in","r",stdin);
	get(n);
	rep(1,n,i)
	{
		int get(l),get(r)+l;
		t[i]=(wy){l,r};
		b[++cnt]=l;b[++cnt]=r;
	}
	sort(b+1,b+1+cnt);
	rep(1,cnt,i)if(i==1||b[i]!=b[i-1])b[++m]=b[i];
	rep(1,n,i)
	{
		t[i].l=lower_bound(b+1,b+1+m,t[i].l)-b;
		t[i].r=lower_bound(b+1,b+1+m,t[i].r)-b;
		rep(1,t[i].l,j)rep(t[i].r,m,k)++c[j][k];
		//cout<<t[i].l<<' '<<t[i].r<<endl;
	}
	memset(f,0xcf,sizeof(f));
	memset(g,0xcf,sizeof(g));
	f[1][0]=0;g[m][0]=0;
	rep(2,m,i)rep(0,c[1][i],j)
	{
		rep(1,i-1,k)
		{
			f[i][j]=max(f[i][j],f[k][j]+c[k][i]);
			if(j>=c[k][i])f[i][j]=max(f[i][j],f[k][j-c[k][i]]);
		}
		//cout<<i<<' '<<j<<' '<<f[i][j]<<endl;
	}
	fep(m-1,1,i)rep(0,c[i][m],j)rep(i+1,m,k)
	{
		g[i][j]=max(g[i][j],g[k][j]+c[i][k]);
		if(j>=c[i][k])g[i][j]=max(g[i][j],g[k][j-c[i][k]]);
	}
	rep(1,m,i)
	{
		rep(i,m,j)
		{
			int y=n;
			rep(0,n,x)
			{
				while(y&&min(c[i][j]+x+y,f[i][x]+g[j][y])<=min(c[i][j]+x+y-1,f[i][x]+g[j][y-1]))--y;
				ans[i][j]=max(ans[i][j],min(c[i][j]+x+y,f[i][x]+g[j][y]));
			}
			//cout<<i<<' '<<j<<' '<<ans[i][j]<<endl;
		}
	}
	int ww=0;
	rep(1,n,i)ww=max(ww,min(i,f[m][i]));
	put(ww);
	rep(1,n,i)
	{
		ww=0;
		rep(1,t[i].l,j)rep(t[i].r,m,k)ww=max(ww,ans[j][k]);
		put(ww);
	}
	return 0;
}