【 2019北京集训测试赛(十)】 子矩阵 二分

题目大意:给你一个n×n的矩阵,请在这个矩阵中找出一个子矩阵(x1,y1)(x2,y2),使得x2i=x1y2j=y1a[i][j]2×(x2x1+y2y1)最大。

数据范围:n500|A|109

 

我们考虑二分这个答案

设答案的分母为S,分子为d,当前二分到的数是ans

如果最终的答案可以大于ans,则有Sd>ans

 

我们移项,则有S>2ans(Δx+Δy)

我们考虑枚举Δx,然后找出一个宽为x,满足上面约束的子矩阵。

Fx[i][j]=x1k=0a[i+k][j]

对于i[1,nx+1],若在Fx[i]中找到满足条件的矩阵,那么你会找到j1j2,满足:

ans×x>j2j=j1(Fx[i][j]ans)

简单变式一下,则有:

ans×x>j2j=1(Fx[i][j]ans)j11j=1(Fx[i][j]ans)

我们可以通过维护j2j=1(Fx[i][j]ans)的最小值和最大值在O(n2)的时间内,完成给定Δx和给定ans的判断。

加上二分ans的过程,我们可以在O(n2log(ϵ))的时间复杂度内,求出给定Δx情况下的最大ans

整个做法的复杂度也就是O(n3log(ϵ)),显然过不去。

 

我们考虑到,一个长度为n的随机序列的最长上升子序列期望长度是O(log n)

我们考虑将需要判断的序列X随机打乱。

我们先将序列进行修改,然后基于之前求出的ans,求一遍答案,判断修改后的序列是否会更加优秀。

如果更加优秀,我们就重新二分出答案。

不难发现,重新二分答案的次数期望是O(log n)的。

总的复杂度就是O(n3+n2lognlog(ε))的。

完结撒花

 

复制代码
 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 @   AlphaInf  阅读(221)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示