1. 程式人生 > 其它 >CF1399F Yet Another Segments Subset 題解

CF1399F Yet Another Segments Subset 題解

把貢獻看成邊,那麼會形成一個樹形結構。有一個做法就是在樹上做 dp 然後樹狀陣列優化,\(O(n^2\log n)\)

我的做法是,考慮一個區間 dp,設 \(f_{l,r}\) 表示所有在 \([l,r]\) 內的線段最多選多少個,這樣就有一個 \(O(n^3)\) 的 dp。考慮什麼情況的轉移是有用的:對於區間 \([l,r]\) 來說,只有左端點在 \(l\) 和右端點在 \(r\) 的線段的端點是可以轉移的。每個線段只會在 \(O(n)\) 個區間轉移,所以複雜度 \(O(n^2)\)

還有一個常數優化是,其實只需要管左端點在 \(l\) 的,剩下的那種情況一定能求到。

點選檢視程式碼
#include<cstdio>
#include<algorithm>
#include<vector>
#define pb push_back
inline int max(const int &a,const int &b){return a>b?a:b;}
const int N=6000+13;
struct Node{int l,r;}a[N];
int n,b[N],f[N][N];
std::vector<int> g[N];
int main(){int T;scanf("%d",&T);while(T--){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d%d",&a[i].l,&a[i].r);
		b[i*2-1]=a[i].l,b[i*2]=a[i].r;
	}
	std::sort(b+1,b+n*2+1);int tt=std::unique(b+1,b+n*2+1)-b-1;
	for(int i=1;i<=tt;++i) g[i].clear();
	for(int i=1;i<=tt;++i)
		for(int j=i;j<=tt;++j) f[i][j]=0;
	for(int i=1;i<=n;++i){
		int l=std::lower_bound(b+1,b+tt+1,a[i].l)-b;
		int r=std::lower_bound(b+1,b+tt+1,a[i].r)-b;
		f[l][r]=1;g[l].pb(r);
	}
	for(int i=1;i<=tt;++i) std::sort(g[i].begin(),g[i].end());
	for(int l=tt-1;l;--l){
		for(int r=l+1;r<=tt;++r){
			int tmp=max(f[l+1][r],f[l][r-1]);
			for(int i=0,lim=g[l].size();i<lim;++i){
				int p=g[l][i];
				if(p+1<=r) tmp=max(tmp,f[l][p]+f[p+1][r]);
				else break;
			}
			f[l][r]+=tmp;
		}
	}
	printf("%d\n",f[1][tt]);
}
	return 0;
}