[NOI2011]Noi嘉年华

 [NOI2011]Noi嘉年华 

把若干区间分成两个集合(可以有的区间不放在任意集合中),使得任意的a∈A,b∈B,a、b区间没有交集

求较小集合最大能是多少

n<=200

区间不交,直接处理只能状压了吧。。。排序什么的都没有用

但是发现,区间一定是一段黑色,一段白色

n<=200

把一段直接分配给某种颜色!

离散化先,

in[l][r]完全在(l,r)中的区间

pre[i][j],(1,i),某个集合选择了j个,另一个集合最大选择多少。转移时候枚举最后一段都给一个集合

bac[i][j],(i,T),.....同理

第一问就是:max(pre[T][i],i)

第二问:

强制必须选择,那么这个区间一定被某个连续段包含。枚举连续段

先设:f[l][r],强制完全在[l,r]中的区间都给某个集合时,最小的集合最大能是多少

枚举左右各选择几个,用min(x+in[l][r]+y,pre[l][x]+bac[r][y])更新

O(n^4)

发现x不变时,y作为自变量,x+in[l][r]+y是增函数,而pre[l][x]+bac[r][y]是减函数

所以,x变大,y必须变小,整个min才可能更大

决策单调性!

O(n^3logn)

进一步发现,整个min函数是单峰的函数,所以下一个不优的时候直接break

对y维护指针平移。

(一般决策单调性绝对不能这么做)

O(n^3)

Code:

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('
');}

namespace Miracle{
const int N=402;
const int inf=0x3f3f3f3f;
int n,l[N],r[N],c[N],tot;
int in[N][N],pre[N][N],bac[N][N],f[N][N];
ll ans=0;
int main(){
    rd(n);
    for(reg i=1;i<=n;++i){
        rd(l[i]);rd(r[i]);
        r[i]=l[i]+r[i];
        c[++tot]=l[i];c[++tot]=r[i];
    }
    sort(c+1,c+tot+1);
    tot=unique(c+1,c+tot+1)-c-1;
    for(reg i=1;i<=n;++i){
        l[i]=lower_bound(c+1,c+tot+1,l[i])-c;
        r[i]=lower_bound(c+1,c+tot+1,r[i])-c;
//        cout<<l[i]<<" and "<<r[i]<<endl;
    }
    for(reg i=1;i<=tot;++i){
        for(reg j=i+1;j<=tot;++j){
            for(reg k=1;k<=n;++k){
                if(l[k]>=i&&r[k]<=j) ++in[i][j];
            }
        }
    }
//    for(reg i=1;i<=tot;++i){
//        for(reg j=1;j<=tot;++j){
//            cout<<i<<" "<<j<<" : "<<in[i][j]<<endl;
//        }
//    }
    memset(pre,-inf,sizeof pre);
    memset(bac,-inf,sizeof bac);
    pre[1][0]=0;
    for(reg i=1;i<=tot;++i){
        for(reg j=0;j<=n;++j){
            for(reg k=1;k<i;++k){
                pre[i][j]=max(pre[i][j],pre[k][j]+in[k][i]);
                if(j>=in[k][i]) pre[i][j]=max(pre[i][j],pre[k][j-in[k][i]]);
            }
//            cout<<" i j "<<i<<" "<<j<<" : "<<pre[i][j]<<endl;
        }
    }
//    cout<<"---------------------------------------------------------"<<endl;
    bac[tot][0]=0;
    for(reg i=tot;i>=1;--i){
        for(reg j=0;j<=n;++j){
            for(reg k=i+1;k<=tot;++k){
                bac[i][j]=max(bac[i][j],bac[k][j]+in[i][k]);
                if(j>=in[i][k]) bac[i][j]=max(bac[i][j],bac[k][j-in[i][k]]);
            }
//            cout<<" i j "<<i<<" "<<j<<" : "<<bac[i][j]<<endl;
        }
    }
    
    for(reg j=0;j<=n;++j){
        ans=max(ans,(ll)min(j,pre[tot][j]));
    }
    cout<<ans<<endl;
    
    for(reg l=1;l<=tot;++l){
        for(reg r=l+1;r<=tot;++r){
            int y=n;
            for(reg x=0;x<=n;++x){
                while(y&&min(x+in[l][r]+y,pre[l][x]+bac[r][y])<=min(x+in[l][r]+y-1,pre[l][x]+bac[r][y-1])) --y;
//                cout<<" x y "<<x<<" "<<y<<endl;
                f[l][r]=max(f[l][r],min(x+in[l][r]+y,pre[l][x]+bac[r][y]));
            }        
//            cout<<" l r "<<l<<" "<<r<<" : "<<f[l][r]<<endl;
        }
    }
    for(reg i=1;i<=n;++i){
        int L=l[i],R=r[i];
        int mx=0;
        for(reg l=L;l>=1;--l){
            for(reg r=R;r<=tot;++r){
                mx=max(mx,f[l][r]);
            }
        }
        cout<<mx<<endl;
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/4/9 10:31:40
*/
View Code

 处理区间不交等问题时候,直接做要状压

不妨强制一段都颜色一致,暴力枚举转移

原文地址:https://www.cnblogs.com/Miracevin/p/10681064.html