cf Maximum Subrectangle
http://codeforces.com/contest/1060/problem/C
矩形中的和只和n,m数列在这上面的分别的和有关。比如1,2,3和7,8,9交叉形成的和=(1+2+3)*(7+8+9)=144。
而两个的交叉是分别的选择,无相互影响。
那么,策略就是,我们先找到对于数列n来说的一个目标数列n_sum[],n_sum[i]记录着n中所有连续长度为i的段中,和最小是多少。举例来说:n=[3,4,2,2],那么n_sum=[0,2,4,8,11],其中下标3的数是8,代表n中长为3的连续子序列中最小和是8。
怎么得到的后面解释,先把有这个n_sum和m_sum为前提。注意这两个数列中的数值是递增的。
有了n_sum和m_sum,n_sum的每个下标i,找到m_sum[j]*n_sum[i]<x就行了。因为两个数列中的数值是递增的,用双指针记录位置,可用o(n)做完。这样等于穷举所有长度搭配的可能。每次找到后,用ans=max(ans,i*j)就行了。
至于怎么找每个长度的最小和,先复制n中内容到temp,先找长度1,等于min(n),然后每次temp[i]=n[i]+temp[i+1]就逐次累加长度了。这是o(n^2)复杂度的。
python太慢过不了,用c++重写了一遍,花了一个半小debug的问题竟然又是出在爆int上,哎~
int是32bit 最大10^10
python版:
n, m = [int(x) for x in input().split()] ns = [int(x) for x in input().split()] ms = [int(x) for x in input().split()] k = int(input()) def getlen(arr): ans = [0] start = arr[:] ans.append(min(arr)) for l in range(2, len(arr) + 1): minn = 10**16 for i in range(len(arr) - l + 1): start[i] = arr[i] + start[i + 1] minn = min(minn, start[i]) ans.append(minn) return ans nl=getlen(ns) ml=getlen(ms) j=0 ans=0 for i in reversed(range(n+1)): while j<m+1 and ml[j]*nl[i]<=k: j+=1 ans=max(ans,i*(j-1)) print(ans)
c++版:
#include <iostream> #include <vector> #include <cstring> #include <algorithm> using namespace std; #define ll long long vector<ll> getlen(ll* a,ll l){ vector<ll> ans; ans.push_back(0); ll start[l]; memcpy(start, a, l*sizeof(ll)); ll minn=INT_MAX; for(ll i =0;i<l;i++) minn=min(minn,start[i]); ans.push_back(minn); for(ll k=2;k<=l;k++){ minn=INT_MAX; for(ll i=0;i<=l-k;i++){ start[i]=a[i]+start[i+1]; minn=min(minn,start[i]); } ans.push_back(minn); } return ans; } int main(int argc, char** argv) { ll n,m; cin>>n>>m; ll ns[n]; ll ms[m]; for(ll i=0;i<n;i++){ cin>>ns[i]; } for(ll i=0;i<m;i++){ cin>>ms[i]; } ll k; cin>>k; vector<ll> nl=getlen(ns,n); vector<ll> ml=getlen(ms,m); ll ans=0; ll j=1; for(ll i=n;i>0;i--){ while(j<m+1 && ml[j]*nl[i]<=k)j++; ans=max(ans,i*(j-1)); } cout<<ans; return 0; }