【 2019北京集训测试赛(十)】 子矩阵 二分
题目大意:给你一个$n\times n$的矩阵,请在这个矩阵中找出一个子矩阵$(x_1,y_1)$,$(x_2,y_2)$,使得$\dfrac{\sum\limits_{i=x_1}^{x_2} \sum\limits_{j=y_1}^{y_2}a[i][j] }{2\times(x_2-x_1+y_2-y_1)}$最大。
数据范围:$n≤500$,$|A|≤10^9$
我们考虑二分这个答案
设答案的分母为$S$,分子为$d$,当前二分到的数是$ans$。
如果最终的答案可以大于$ans$,则有$\frac{S}{d}>ans$
我们移项,则有$S>2ans(\Delta x+\Delta y)$
我们考虑枚举$\Delta x$,然后找出一个宽为$x$,满足上面约束的子矩阵。
令$F_x[i][j]=\sum\limits_{k=0}^{x-1} a[i+k][j]$
对于$i∈[1,n-x+1]$,若在$F_x[i]$中找到满足条件的矩阵,那么你会找到$j_1$和$j_2$,满足:
$ans\times x>\sum\limits_{j=j_1}^{j_2} (F_x[i][j]-ans)$
简单变式一下,则有:
$ans\times x>\sum\limits_{j=1}^{j_2}(F_x[i][j]-ans)-\sum\limits_{j=1}^{j_1-1}(F_x[i][j]-ans)$
我们可以通过维护$\sum\limits_{j=1}^{j_2}(F_x[i][j]-ans)$的最小值和最大值在$O(n^2)$的时间内,完成给定$\Delta x$和给定$ans$的判断。
加上二分$ans$的过程,我们可以在$O(n^2log(-\epsilon))$的时间复杂度内,求出给定$\Delta x$情况下的最大$ans$。
整个做法的复杂度也就是$O(n^3log(-\epsilon))$,显然过不去。
我们考虑到,一个长度为$n$的随机序列的最长上升子序列期望长度是$O(\log\ n)$的
我们考虑将需要判断的序列X随机打乱。
我们先将序列进行修改,然后基于之前求出的$ans$,求一遍答案,判断修改后的序列是否会更加优秀。
如果更加优秀,我们就重新二分出答案。
不难发现,重新二分答案的次数期望是$O(\log\ n)$的。
总的复杂度就是$O(n^3+n^2\log n\log(-\varepsilon))$的。
完结撒花
1 #include<bits/stdc++.h> 2 #define M 505 3 #define eps 1e-8 4 #define L long long 5 #define INF 3e14 6 #define _y1 orzmyh 7 using namespace std; 8 9 L a[M][M]={0}; 10 int p[M]={0},n; 11 12 int _x1,_x2,_y1,_y2; 13 int _X1,_X2,_Y1,_Y2; 14 bool check(int x,double ans){ 15 for(int i=1;i<=n-x+1;i++){ 16 double minn=0,now=0; int minid=0; 17 for(int j=1;j<=n;j++){ 18 now+=a[i+x-1][j]-a[i-1][j]-ans; 19 if(minn>now){ 20 minn=now; 21 minid=j; 22 } 23 if(now-minn>=ans*x){ 24 _x1=i; _x2=i+x-1; 25 _y1=minid+1; _y2=j; 26 return 1; 27 } 28 } 29 } 30 return 0; 31 } 32 bool ok(double l,double r){ 33 return (r-l>eps)&&(((r-l)/l)>eps); 34 } 35 36 int main(){ 37 // freopen("in.txt","r",stdin); 38 double ans=0,maxn=-INF; 39 scanf("%d",&n); 40 for(int i=1;i<=n;i++) 41 for(int j=1;j<=n;j++){ 42 scanf("%lld",&a[i][j]); 43 maxn=max(maxn,1.*a[i][j]); 44 } 45 if(maxn<=0){ 46 printf("%.10lf\n",maxn/4); 47 for(int i=1;i<=n;i++) 48 for(int j=1;j<=n;j++) 49 if(a[i][j]==maxn){ 50 printf("%d %d\n",i,j); 51 printf("%d %d\n",i,j); 52 return 0; 53 } 54 return 0; 55 } 56 for(int i=1;i<=n;i++) 57 for(int j=1;j<=n;j++) a[i][j]+=a[i-1][j]; 58 59 for(int i=1;i<=n;i++) p[i]=i; 60 random_shuffle(p+1,p+n+1); 61 62 63 for(int i=1;i<=n;i++){ 64 int x=p[i]; 65 if(!check(x,ans)) continue; 66 double l=ans,r=INF; 67 while(ok(l,r)){ 68 double mid=(l+r)/2.; 69 if(check(x,mid)) l=mid; 70 else r=mid; 71 } 72 ans=l; 73 _X1=_x1; _X2=_x2; 74 _Y1=_y1; _Y2=_y2; 75 } 76 printf("%.10lf\n",ans/2); 77 printf("%d %d\n",_X1,_Y1); 78 printf("%d %d\n",_X2,_Y2); 79 }