【 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)}$最大。c++

數據範圍:$n≤500$,$|A|≤10^9$dom

 

咱們考慮二分這個答案spa

設答案的分母爲$S$,分子爲$d$,當前二分到的數是$ans$。code

若是最終的答案能夠大於$ans$,則有$\frac{S}{d}>ans$blog

 

咱們移項,則有$S>2ans(\Delta x+\Delta y)$it

咱們考慮枚舉$\Delta x$,而後找出一個寬爲$x$,知足上面約束的子矩陣。class

令$F_x[i][j]=\sum\limits_{k=0}^{x-1} a[i+k][j]$im

對於$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)$di

簡單變式一下,則有:

$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 }
相關文章
相關標籤/搜索