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;
}