UOJ Logo kczno1的博客

博客

牛宫的n^3做法

2017-07-08 18:33:24 By kczno1

题目:https://www.luogu.org/problem/show?pid=1565

原题解似乎是 $O(n^3 * \log n)$ 的

但这题有$ O(n^3) $的做法。

虽然 https://www.luogu.org/record/show?rid=2494227 我这个$ O(n^3*\log n) $的做法跑的比我的$ O(n^3) $还快。。(应该是数据问题)

就是枚举上下边界$ x_1 $,$ x_2 $,然后枚举右端点 $ i $,

那么就是求最左的点 $ j $ 满足 $ sum_j < sum_i $ ( $ sum_i $ 表示 $ x=x_1...x_2,\,y=0...i $ 这个矩形的面积和)

如果 $ j_1 < j_2 $ 且 $ sum_{j_1} < sum_{j_2} $ ,那么 $j_2 $ 不可能成为左端点。

所以可以用单调栈维护可能的左端点,查询时二分。

$ O(n^3) $的做法:

(1) https://www.luogu.org/record/show?rid=2498503

枚举上下边界,实际上不只是可能的左端点在单调栈上,可以同理证明可能的右端点在相反的单调栈上。

从左到右枚举右端点(只枚举单调栈上的),左端点的变化是单调的。于是可以$ O(n) $

(2) https://www.luogu.org/record/show?rid=2498807

感觉这是理论最快的做法。

原理是:当上下区间长度不变时 就是要求左右区间长度的最大值 $ mx $ , 而 $mx$ 最多变大 $ n $ 次。

长度从大到小地枚举上下边界(其实从小到大也一样),再枚举右端点 $ i $ ,

如果以$i$为右端点的区间长度的最大值大于当前的$ mx $ ,那么 $ sum_i>min\,\, sum_j | j=[0,i-mx] $,这两者等价

所以维护这个$min$,如果发现$sum_i>min$,就++$ mx $,之后暴力重新$ O(n) $算出 $ min $。

如果上下区间长度从大到小枚举,其实长度不变或变小时都不用修改$mx$,于是最多重算$O(n)$次$min$。

从小到大枚举的话,当长度变大时需要将$mx$清0,于是最多重算$O(n^2)$次$min$。

总之总复杂度都是$O(n^3)$

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(int i=l;i<=r;++i)
template<class T>inline void chmax(T &x,const T &y)
{
    if(x<y)x=y;
}
template<class T>inline void chmin(T &x,const T &y)
{
    if(x>y)x=y;
}
typedef long long ll;
const int N=205;
ll s[N][N];
int ans;
ll a,_a;int w,_w;
#define get(y) (s[x2][y]-s[x1][y])

int main()
{
    freopen("1.in","r",stdin);freopen("2.out","w",stdout);
    int n,m;
    cin>>n>>m;
    rep(i,1,n)
    {
      rep(j,1,m)
      { 
        scanf("%lld",s[i]+j);
        s[i][j]+=s[i][j-1];
      }
      rep(j,1,m)
        s[i][j]+=s[i-1][j];
    }
    int mx=1;
    for(int t=n;t;--t)
    {
        int x1=0,x2=t;
      do
      {
          int i=mx;ll mn=0;//mn=min(get(0)..get(i-mx))
         do
         {
             chmin(mn,get(i-mx));
           while(get(i)>mn&&mx<=i)
           {++mx;
            mn=0;
            rep(j,1,i-mx) chmin(mn,get(j));
           }
         }while(++i<=m);
      }while(++x1,++x2<=n);
       chmax(ans,t*(mx-1));
    }
    printf("%d\n",ans);
}

评论

Alextokc
@JOHNKRAM
Alextokc
@JOHNKRAM
JOHNKRAM
xhn2333
%%%孔爷

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。