【 2019北京集训测试赛(十)】 子矩阵 二分
题目大意:给你一个n×n的矩阵,请在这个矩阵中找出一个子矩阵(x1,y1),(x2,y2),使得x2∑i=x1y2∑j=y1a[i][j]2×(x2−x1+y2−y1)最大。
数据范围:n≤500,|A|≤109
我们考虑二分这个答案
设答案的分母为S,分子为d,当前二分到的数是ans。
如果最终的答案可以大于ans,则有Sd>ans
我们移项,则有S>2ans(Δx+Δy)
我们考虑枚举Δx,然后找出一个宽为x,满足上面约束的子矩阵。
令Fx[i][j]=x−1∑k=0a[i+k][j]
对于i∈[1,n−x+1],若在Fx[i]中找到满足条件的矩阵,那么你会找到j1和j2,满足:
ans×x>j2∑j=j1(Fx[i][j]−ans)
简单变式一下,则有:
ans×x>j2∑j=1(Fx[i][j]−ans)−j1−1∑j=1(Fx[i][j]−ans)
我们可以通过维护j2∑j=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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!