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