【NOIP2016提高A組模擬10.15】最大化
阿新 • • 發佈:2018-05-21
pan include scanf 沒有 algorithm %d 矩陣 枚舉 tdi
題目
分析
枚舉兩個縱坐標i、j,接著表示枚舉區域的上下邊界,
設對於每個橫坐標區域的前綴和和為\(s_l\),枚舉k,
顯然當\(s_k>s_l\)時,以(i,k)為左上角,(j,k)為右下角的矩陣一定合法。
k從小到大,維護一個單調隊列,
顯然當\(l1<l2\)時
如果\(s_{l1}<s_{l2}\),l2一定對答案沒有貢獻,就不將其加入單調隊列。
對於一個k,在單調隊列中二分,枚舉出一個最小的位置,並且\(s_k>s_l\)。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <queue> #include <cmath> const int N=305; using namespace std; long long sum[N][N],num[N]; int d[N],tot,n,m; int ans; inline int read(long long &n) { char ch=' '; int q=0,w=1; for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar()); if(ch=='-') w=-1,ch=getchar(); for(;ch>='0' && ch<='9';ch=getchar()) q=q*10+ch-48;n=q*w; return n; } inline long long sum1(int x,int y,int x1,int y1) { return sum[x1][y1]-sum[x-1][y1]-sum[x1][y-1]+sum[x-1][y-1]; } int main() { scanf("%d%d",&n,&m); int i,j,k; long long p; for(i=1;i<=n;i++) for(j=1;j<=m;j++) { read(sum[i][j]); sum[i][j]=sum[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]; } ans=0; int l=1,r=tot,mid; for(i=1;i<=n;i++) for(j=n;j>=i && (j-i+1)*m>ans;j--) { int e=j-i+1; tot=1; for(k=1;k<=m;k++) { p=sum1(i,1,j,k); if((j-i+1)*k>ans) { l=1; r=tot; while(l<r) { mid=(l+r)/2; if(num[mid]<p) r=mid; else l=mid+1; } if(num[l]<p) ans=max(ans,e*(k-d[l])); } if(p<num[tot]) { d[++tot]=k; num[tot]=p; } } } printf("%d",ans); }
【NOIP2016提高A組模擬10.15】最大化