【 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 }
posted @ 2019-04-15 14:31  AlphaInf  阅读(218)  评论(0编辑  收藏  举报