CF 1510 H. 难优化问题的解决
CF 1510 H. Hard Optimization 题解
首先可以很容易发现所有线段的端点一定是属于一开始的\(2n\)个点。不然一定可以通过一些简单的调整来达到目标。
同时由于线段之间的关系要么包含,要么不交,所以我们可以将这些线段建成一个森林,每一条线段连向最短的包含它的线段。
然后就可以考虑树形dp了。
对于\(i\)的子树,被它包含的区间的端点将\(i\)号区间分成了\(k\)个线段,记为\(c_1,c_2...c_k\),则\(i\)的选择一定是若干连续的线段:\(c_l,c_{l+1}...c_{l+z}\)。
但是同时需要满足条件:所选择的\(c_j\)不能被其它节点(属于\(i\)的子树)的线段覆盖。显然我们无法将子树内所选区间的信息传递上来,所以只能在处理子树内的节点是给上面的祖先预留线段。
合并两个子树的信息,只需要将两边的预留区间个数加起来,并且讨论一下中间是否需要合并两段预留区间。同时对于一个节点,它需要拿走一个预留区间(具体是什么并不重要),同时也可以给上面的区间再预留几段。
具体实现
设\(dp[i][j][a][b]\),表示\(i\)的子树给上面预留了\(j\)个线段,\(a\)表示是否有预留的线段包含左端点,\(b\)表示是否有预留的线段包含右端点,的最大长度之和。
合并\(dp[u][j][a][b],dp[v][j'][a'][b']\):
- \(\text{merge}(dp[u][j][a][b],dp[v][j'][a'][b'])\rightarrow dp[fa][j+j'-y][a][0]\),其中\(-y\)表示是否将中间的部分合并(条件:\(a'=b=\text{true}\))。
- \(\text{merge}(dp[u][j][a][b],dp[v][j'][a'][b'])+seg[v].l-seg[u].r\rightarrow dp[fa][j+j'+1-y-z][a][1]\),其中\(+1\)表示给上面预留了\([seg[u].r,seg[v].l]\)这段区间(也就是线段\(u\)和\(v\)之间的空缺部分),\(-z\)表示新加的区间是否和\(v\)的右端合并,\(-y\)和上面定义一样。
这个部分可以树上背包转移。
\(i\)取走一个之前预留的区间
大多数情况下只需要\(dp[i][j][a][b]\rightarrow dp[i][j-1][a][b]\)就行了,但是当\(j=2,1\)时会比较特殊。
- \(dp[i][2][1][1]\rightarrow dp[i][1][0][1],dp[i][1][1][0]\)
- \(dp[i][1][0][1],dp[i][1][1][0]\rightarrow dp[i][0][0][0]\)
其实上面转移的存在条件就是中间的被取完了,只能取旁边的了。
可以证明只有上面的情况是特殊的,其它均满足\(dp[i][j][a][b]\rightarrow dp[i][j-1][a][b]\)这样的转移。
证明: 两边的区间的灵活性比中间好,可以选择单独作为一段区间或者和别的合并,所以优先取中间的不会边劣。
输出方案
这个部分是这题最烦的地方。
由于dp的定义比较特殊,是用的预留,同时还有一些合并,所以没法用正常记录前驱的方法记录方案。
我采用的方法是这样的:
\(l[i][j][a][b]\)表示\(dp[i][j][a][b]\)这个状态中包含左端点的预留区间是什么,如果\(a=0\),则\(l[i][j][a][b]=\empty\)
\(r[i][j][a][b]\)表示包含右端点的段,与上面类似。
对于每一个点找到它预留给上面的线段,然后答案集合就是所有点预留集合的并,然后贪心地分配线段即可(从小到大分配)。
由于这题空间有一点卡常,所以我并没有记录树上背包的中间值,而是输出路径的时候每一个点都重新计算了一遍。
时间复杂度
每一个dp状态\(dp[i][j][a][b]\)中的\(j\)应该满足\(j\leq \min(\text{depth[i]},\text{size[i]})\)。所以相当于任意两个节点会再lca的地方产生贡献,所以是\(O(N^2)\)的。
我的code:
#define record(a,b,c,d) II(II(seg[a].FIR,record[a][b][c][d].FIR),II(record[a][b][c][d].SEC,seg[a].SEC))
using namespace std;
inline int read(){
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
const int MAXN=2e3+1;
int n,id[MAXN],siz[MAXN],depth[MAXN];
mp ans[MAXN];
bool used[MAXN];
mp seg[MAXN];
vector<int> roots,g[MAXN];
bool cmp(int A,int B){
return seg[A].FIR<seg[B].FIR;
}
bool cmp2(int A,int B){
return seg[A].SEC-seg[A].FIR<seg[B].SEC-seg[B].FIR;
}
bool cmp3(mp A,mp B){
return A.SEC-A.FIR<B.SEC-B.FIR;
}
void build_tree(){
rb(i,1,n) id[i]=i;
sort(id+1,id+1+n,cmp);
vector<int> tmp;
rb(i_,1,n){
int i=id[i_],_=-INF;
rb(j_,i_+1,n){
int j=id[j_];
if(seg[j].SEC>seg[i].SEC) break;
if(seg[j].FIR>_){
g[i].PB(j);
tmp.PB(j);
_=seg[j].SEC;
}
}
}
sort(ALL(tmp));
rb(i,1,n){
auto ite=lower_bound(ALL(tmp),i);
if(ite==tmp.end()||*ite!=i) roots.PB(i);
}
}
vector<mp> rest;
void recover(vector<mp> v){
sort(ALL(v),cmp3);
sort(id+1,id+1+n,cmp2);
rb(i_,1,n){
int i=id[i_];
rep(j,n){
if(!used[j]&&v[j].SEC<=seg[i].SEC&&v[j].FIR>=seg[i].FIR){
ans[i]=v[j];
used[j]=1;
break;
}
}
}
rb(i,1,n) printf("%d %d\n",ans[i].FIR,ans[i].SEC);
}
namespace DP{
int dp[MAXN][MAXN][2][2];
int f[MAXN][MAXN][2][2];
int fi[MAXN][MAXN][2][2];
mp record[MAXN][MAXN][2][2];
struct D{
short j;
bool a,b;
D(){}
D(int B,bool C,bool Z){
j=B,a=C,b=Z;
}
};
pair<D,D> pre[MAXN][MAXN][2][2];
mp merge(mp A,mp B){
return II(A.FIR,B.SEC);
}
void dfs(int now){
siz[now]=1;
for(auto it:g[now]) depth[it]=depth[now]+1,dfs(it),siz[now]+=siz[it];
}
void init(){
rb(i,1,n) rb(j,0,n) rep(a,2) rep(b,2) record[i][j][a][b].FIR=seg[i].FIR,record[i][j][a][b].SEC=seg[i].SEC;
memset(f,-63,sizeof(f));
memset(dp,-63,sizeof(dp));
for(auto it:roots) dfs(it);
rb(i,1,n) check_min(siz[i],depth[i]);
}
void trans(int now){
if(g[now].empty()){
dp[now][0][0][0]=seg[now].SEC-seg[now].FIR;
return;
}
int RP=seg[now].FIR;
vector<int> c;
sort(ALL(g[now]),cmp);
for(auto it:g[now]) trans(it),c.PB(seg[it].FIR-RP),RP=seg[it].SEC;
c.PB(seg[now].SEC-RP);
rb(i,1,g[now].size()) memset(f[i],-63,sizeof(f[i]));
f[0][0][0][0]=0;
f[0][1][1][1]=c[0];
int tot=1;
rb(i,1,g[now].size()){
rb(j,0,tot){
rep(a1,2){
rep(a2,2){
if(f[i-1][j][a1][a2]>=0){
rb(k,0,siz[g[now][i-1]]){
rep(a3,2){
rep(a4,2){
if(dp[g[now][i-1]][k][a3][a4]>=0){
int val=f[i-1][j][a1][a2]+dp[g[now][i-1]][k][a3][a4];
for(int fk=0;fk<=(a2&&a3);fk++){
check_max(f[i][j+k-fk][a1][0],val);
check_max(f[i][j+k+1-fk][a1][1],val+c[i]);
if(a4)
check_max(f[i][j+k-fk][a1][1],val+c[i]);
}
}
}
}
}
}
}
}
}
tot+=siz[g[now][i-1]];
}
rb(i,0,tot) rep(a,2) rep(b,2) dp[now][i][a][b]=f[g[now].size()][i+1][a][b];
rb(i,0,1) rep(a,2) rep(b,2){
if(i==0){
if(a||b){
check_max(dp[now][i][0][0],dp[now][i][a][b]);
dp[now][i][a][b]=-INF;
}
}
else{
if(a&&b){
rep(na,2){
rep(nb,2){
if(!(na&&nb)){
check_max(dp[now][i][na][nb],dp[now][i][a][b]);
}
}
}
dp[now][i][a][b]=-INF;
}
}
}
}
void work(){
LL answer=0;
for(auto it:roots) trans(it),answer+=dp[it][0][0][0];
printf("%lld\n",answer);
}
void get(int now){
if(g[now].empty()){
return;
}
for(auto it:g[now]) get(it);
vector<int> c;
int RP=seg[now].FIR;
for(auto it:g[now]) c.PB(seg[it].FIR-RP),RP=seg[it].SEC;
c.PB(seg[now].SEC-RP);
rb(i,1,g[now].size()) memset(f[i],-63,sizeof(f[i]));
f[0][0][0][0]=0;
f[0][1][1][1]=c[0];
int tot=1;
rb(i,1,g[now].size()){
rb(j,0,tot){
rep(a1,2){
rep(a2,2){
if(f[i-1][j][a1][a2]>=0){
rb(k,0,siz[g[now][i-1]]){
rep(a3,2){
rep(a4,2){
if(dp[g[now][i-1]][k][a3][a4]>=0){
for(int fk=0;fk<=(a2&&a3);fk++){
int val=f[i-1][j][a1][a2]+dp[g[now][i-1]][k][a3][a4];
if(val>f[i][j+k-fk][a1][0]){
if(i==1){
fi[i][j+k-fk][a1][0]=record(g[now][i-1],k,a3,a4).FIR.SEC-record(g[now][i-1],k,a3,a4).FIR.FIR;
}
else fi[i][j+k-fk][a1][0]=fi[i-1][j][a1][a2];
f[i][j+k-fk][a1][0]=val;
pre[i][j+k-fk][a1][0]=II(D(j,a1,a2),D(k,a3,a4));
}
val+=c[i];
if(val>f[i][j+k+1-fk][a1][1]){
if(i==1){
fi[i][j+k+1-fk][a1][1]=record(g[now][i-1],k,a3,a4).FIR.SEC-record(g[now][i-1],k,a3,a4).FIR.FIR;
}
else fi[i][j+k+1-fk][a1][1]=fi[i-1][j][a1][a2];
f[i][j+k+1-fk][a1][1]=val;
pre[i][j+k+1-fk][a1][1]=II(D(j,a1,a2),D(k,a3,a4));
}
if(a4){
if(val>f[i][j+k-fk][a1][1]){
if(i==1){
fi[i][j+k-fk][a1][1]=record(g[now][i-1],k,a3,a4).FIR.SEC-record(g[now][i-1],k,a3,a4).FIR.FIR;
}
else fi[i][j+k-fk][a1][1]=fi[i-1][j][a1][a2];
f[i][j+k-fk][a1][1]=val;
pre[i][j+k-fk][a1][1]=II(D(j,a1,a2),D(k,a3,a4));
}
}
}
}
}
}
}
}
}
}
}
tot+=siz[g[now][i-1]];
}
rb(i,0,tot){
rep(a,2){
rep(b,2){
if(dp[now][i][a][b]>0){
record[now][i][a][b].FIR=seg[now].FIR;
record[now][i][a][b].SEC=seg[now].SEC;
if(i==0);
else
if(i==1){
if(dp[now][i][a][b]==f[g[now].size()][2][a][b]){
record[now][i][a][b].FIR=seg[now].FIR+c[0]+fi[g[now].size()][i+1][a][b];
D id=pre[g[now].size()][i+1][a][b].SEC;
mp Tmp=record(g[now].back(),id.j,id.a,id.b).SEC;
record[now][i][a][b].SEC=seg[now].SEC-c.back()-(Tmp.SEC-Tmp.FIR);
}
else{
if(dp[now][i][a][b]==f[g[now].size()][2][1][1]){
record[now][i][a][b].FIR=seg[now].FIR+c[0]+fi[g[now].size()][i+1][1][1];
D id=pre[g[now].size()][i+1][1][1].SEC;
mp Tmp=record(g[now].back(),id.j,id.a,id.b).SEC;
record[now][i][a][b].SEC=seg[now].SEC-c.back()-(Tmp.SEC-Tmp.FIR);
}
else{
assert(0);
}
}
}
else{
record[now][i][a][b].FIR=seg[now].FIR+c[0]+fi[g[now].size()][i+1][a][b];
D id=pre[g[now].size()][i+1][a][b].SEC;
mp Tmp=record(g[now].back(),id.j,id.a,id.b).SEC;
record[now][i][a][b].SEC=seg[now].SEC-c.back()-(Tmp.SEC-Tmp.FIR);
}
if(!a) record[now][i][a][b].FIR=seg[now].FIR;
if(!b) record[now][i][a][b].SEC=seg[now].SEC;
}
}
}
}
}
void print(int now,int K,int needa,int needb){
if(g[now].empty()){
rest.PB(seg[now]);
return ;
}
vector<int> c;
int Pre=seg[now].FIR;
for(auto it:g[now]) c.PB(seg[it].FIR-Pre),Pre=seg[it].SEC;
c.PB(seg[now].SEC-Pre);
rb(i,1,g[now].size()) memset(f[i],-63,sizeof(f[i]));
f[0][0][0][0]=0;
f[0][1][1][1]=c[0];
int tot=1;
rb(i,1,g[now].size()){
rb(j,0,tot){
rep(a1,2){
rep(a2,2){
if(f[i-1][j][a1][a2]>=0){
rb(k,0,siz[g[now][i-1]]){
rep(a3,2){
rep(a4,2){
if(dp[g[now][i-1]][k][a3][a4]>=0){
for(int fk=0;fk<=(a2&&a3);fk++){
int val=f[i-1][j][a1][a2]+dp[g[now][i-1]][k][a3][a4];
if(val>f[i][j+k-fk][a1][0]){
if(i==1){
fi[i][j+k-fk][a1][0]=record(g[now][i-1],k,a3,a4).FIR.SEC-record(g[now][i-1],k,a3,a4).FIR.FIR;
}
else fi[i][j+k-fk][a1][0]=fi[i-1][j][a1][a2];
f[i][j+k-fk][a1][0]=val;
pre[i][j+k-fk][a1][0]=II(D(j,a1,a2),D(k,a3,a4));
}
val+=c[i];
if(val>f[i][j+k+1-fk][a1][1]){
if(i==1){
fi[i][j+k+1-fk][a1][1]=record(g[now][i-1],k,a3,a4).FIR.SEC-record(g[now][i-1],k,a3,a4).FIR.FIR;
}
else fi[i][j+k+1-fk][a1][1]=fi[i-1][j][a1][a2];
f[i][j+k+1-fk][a1][1]=val;
pre[i][j+k+1-fk][a1][1]=II(D(j,a1,a2),D(k,a3,a4));
}
if(a4){
if(val>f[i][j+k-fk][a1][1]){
if(i==1){
fi[i][j+k-fk][a1][1]=record(g[now][i-1],k,a3,a4).FIR.SEC-record(g[now][i-1],k,a3,a4).FIR.FIR;
}
else fi[i][j+k-fk][a1][1]=fi[i-1][j][a1][a2];
f[i][j+k-fk][a1][1]=val;
pre[i][j+k-fk][a1][1]=II(D(j,a1,a2),D(k,a3,a4));
}
}
}
}
}
}
}
}
}
}
}
tot+=siz[g[now][i-1]];
}
bool GL,GR;
GL=GR=0;
if(dp[now][K][needa][needb]==f[g[now].size()][K+1][needa][needb]);
else{
assert(K<=1);
if(K==0){
if(dp[now][K][needa][needb]==f[g[now].size()][K+1][0][1]){
needb=1;
GR=1;
}
else{
needa=1;
GL=1;
}
}
else{
if(!needa) GL=1;
else GR=1;
needa=needb=1;
}
}
++K;
vector<mp> V;
vector<pair<mp,mp> > Nex;
if(needb) V.PB(II(seg[now].SEC-c.back(),seg[now].SEC));
rl(i,g[now].size(),1){
pair<D,D> wtf=pre[i][K][needa][needb];
Nex.PB(II(II(g[now][i-1],wtf.SEC.j),II(wtf.SEC.a,wtf.SEC.b)));
if(wtf.SEC.b) V.PB(record(g[now][i-1],wtf.SEC.j,wtf.SEC.a,wtf.SEC.b).SEC);
if(wtf.SEC.a) V.PB(record(g[now][i-1],wtf.SEC.j,wtf.SEC.a,wtf.SEC.b).FIR);
if(wtf.FIR.b) V.PB(II(seg[g[now][i-1]].FIR-c[i-1],seg[g[now][i-1]].FIR));
K=wtf.FIR.j;
needa=wtf.FIR.a;
needb=wtf.FIR.b;
}
reverse(ALL(V));
vector<mp> tmp;
for(auto it:V){
if(tmp.empty()||tmp.back().SEC!=it.FIR) tmp.PB(it);
else tmp[tmp.size()-1]=merge(tmp[tmp.size()-1],it);
}
V=tmp;
rep(i,V.size()){
if(V[i].FIR!=seg[now].FIR&&V[i].SEC!=seg[now].SEC) rest.PB(V[i]);
}
if(GL) rest.PB(V[0]),assert(V[0].FIR==seg[now].FIR);
if(GR) rest.PB(V.back()),assert(V.back().SEC==seg[now].SEC);
for(auto it:Nex) print(it.FIR.FIR,it.FIR.SEC,it.SEC.FIR,it.SEC.SEC);
}
void path(){
for(auto it:roots) get(it);
for(auto it:roots) print(it,0,0,0);
}
};
int main(){
n=read();
rb(i,1,n) seg[i].FIR=read(),seg[i].SEC=read();
build_tree();
DP:: init();
DP:: work();
DP:: path();
assert(rest.size()==n);
recover(rest);
return 0;
}
原文地址:https://www.cnblogs.com/gary-2005/p/14682698.html
推荐阅读