【Luogu】P3933 Chtholly Nota Seniorious

【题意】将n*m矩阵分成两个区域,要求满足一定条件,求两区域内部极差较大值最小。n,m<=2000

【算法】二分

【题解】极差的数值满足单调性,所以考虑二分极差。

对于给定的极差,将所有数值排序后,1~a[n*m]-num-1必须选择A,a[1]+num+1~n*m必须选择B,其它不要求。(开始的时候想二分图染色,后来发现排序一下规律就十分明显了)

现在问题转化为矩阵中已知一些格子选A,一些格子选B,求能否组成合法方案。

观察要求满足的条件,很容易得出结论:分界线必须单调,所以就有上A下B或上B下A,递增或递减,组合成四种情况。

针对上B下A,递增的情况(其他情况类似),对于每一列,找到最下面的B和最上面的A,维护A的递增同时看B是否严格在A上方。

复杂度O(n*m log max(Ai))。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c=='-')t=-1;
    do{s=s*10+c-'0';}while(isdigit(c=getchar()));
    return s*t;
}
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a<b?b:a;}
int abs(int x){return x>0?x:-x;}
void mins(int &a,int b){if(a>b)a=b;}
void maxs(int &a,int b){if(a<b)a=b;}
//void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;}
/*------------------------------------------------------------*/
const int inf=0x3f3f3f3f,maxn=2010;

int n,m,A,B,tot,a[maxn*maxn],c[maxn*maxn],minA[maxn],minB[maxn],maxA[maxn],maxB[maxn];
struct cyc{int id,num;}b[maxn*maxn];
bool cmp(cyc a,cyc b){return a.num<b.num;}
int find(int x,int y){
    int id=c[(x-1)*m+y];
    if(id<A)return 0;
    if(A<=id&&id<B)return 1;
    return 2;
}
bool check(int number)
{
    bool ok=1;int num;
    A=lower_bound(a+1,a+tot+1,a[tot]-number)-a;
    B=upper_bound(a+1,a+tot+1,a[1]+number)-a;
    if(A>B)return 0;
    memset(maxA,0x3f,sizeof(maxA));memset(maxB,0x3f,sizeof(maxB));
    memset(minA,0,sizeof(minA));memset(minB,0,sizeof(minB));
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            if(find(j,i)==0&&maxA[i]==inf)maxA[i]=j;
            if(find(j,i)==2&&maxB[i]==inf)maxB[i]=j;
            if(maxA[i]!=inf&&maxB[i]!=inf)break;
        }
        for(int j=n;j>=1;j--){
            if(find(j,i)==0&&!minA[i])minA[i]=j;
            if(find(j,i)==2&&!minB[i])minB[i]=j;
            if(minA[i]&&minB[i])break;
        }
    }
    num=n+1;ok=1;
    for(int i=1;i<=m;i++){
        num=min(num,maxA[i]);
        if(minB[i]>=num){ok=0;break;}
    }
    if(ok)return 1;
    
    num=n+1;ok=1;
    for(int i=1;i<=m;i++){
        num=min(num,maxB[i]);
        if(minA[i]>=num){ok=0;break;}
    }
    if(ok)return 1;
    
    num=n+1;ok=1;
    for(int i=m;i>=1;i--){
        num=min(num,maxA[i]);
        if(minB[i]>=num){ok=0;break;}
    }
    if(ok)return 1;
    
    num=n+1;ok=1;
    for(int i=m;i>=1;i--){
        num=min(num,maxB[i]);
        if(minA[i]>=num){ok=0;break;}//
    }
    if(ok)return 1;
    
    return 0;
}    
int main()
{
    n=read();m=read();    
    int l=0,r=0,mid;
    for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
        int num=read();r=max(r,num);
        b[++tot]=(cyc){(i-1)*m+j,num};
    }
    sort(b+1,b+tot+1,cmp);
    for(int i=1;i<=tot;i++)c[b[i].id]=i;
    for(int i=1;i<=tot;i++)a[i]=b[i].num;
    while(l<r)
    {
        mid=(l+r)>>1;
        if(check(mid))r=mid;else l=mid+1;
    }
    printf("%d",l);
    return 0;
}
View Code

 

posted @ 2017-10-22 11:47  ONION_CYC  阅读(285)  评论(0编辑  收藏  举报