T1 frame
还是那个老 t r i c k trick trick 每个数取 log \log log 乘法变加法,虽然最后还是要打一个高精。
// 高精的板子
struct Tnum{
#define MAX_LEN 30030
int len, isnagetive;
int num[MAX_LEN];
Tnum() { isnagetive = 0, len = 0; memset(num, 0, sizeof num); }
void get(){
string temp; cin >> temp;
if(temp[0] == '-'){
isnagetive = 1, len = temp.size() - 1;
for(int i = 1; i <= len; i++) num[i] = temp[i] - '0';
} else{
len = temp.size();
for(int i = 1; i <= len; i++) num[i] = temp[i - 1] - '0';
}
}
Tnum add(Tnum a, Tnum b) {
Tnum ans;
ans.len = max(a.len, b.len);
for(int i = 1; i <= ans.len; i++){
int temp = a.num[len - i + 1] + b.num[b.len - i + 1];
ans.num[i] += temp, temp = ans.num[i], ans.num[i] %= 10, ans.num[i + 1] += temp / 10 % 10;
}
if(ans.num[ans.len + 1]) ans.len++;
reverse(ans.num + 1, ans.num + ans.len + 1);
return ans;
}
Tnum minus(Tnum a, Tnum b){
Tnum ans; ans.len = max(a.len, b.len);
for(int i = 1; i <= ans.len; i++){
int temp = 10 + a.num[a.len - i + 1] - b.num[max(0, b.len - i + 1)];
ans.num[i] = temp % 10, a.num[a.len - i] += temp / 10 - 1;
}
while(ans.len > 1 and !ans.num[ans.len]) ans.len--;
reverse(ans.num + 1, ans.num + ans.len + 1);
return ans;
}
Tnum mul(Tnum a, Tnum b){
Tnum ans; ans.len = a.len + b.len;
for(int i = 1; i <= a.len; i++){
int temp = 0;
for(int j = 1; j <= b.len; j++){
temp += a.num[len - i + 1] * b.num[b.len - j + 1] + ans.num[i + j - 1];
ans.num[i + j - 1] = temp % 10, temp /= 10;
}
ans.num[i + b.len] += temp;
}
while(ans.len > 1 and !ans.num[ans.len]) ans.len--;
reverse(ans.num + 1, ans.num + ans.len + 1);
return ans;
}
Tnum abs(Tnum a) { a.isnagetive = 0; return a; }
bool operator == (const Tnum rhs) const {
if(len != rhs.len) return false;
if(isnagetive != rhs.isnagetive) return false;
for(int i = 1; i <= len; i++) if(num[i] != rhs.num[i]) return false;
return true;
}
bool operator < (const Tnum rhs) const {
if(!isnagetive and !rhs.isnagetive){
if(len == rhs.len){
for(int i = 1; i <= len; i++)
if(num[i] != rhs.num[i]) return num[i] < rhs.num[i];
} else return len < rhs.len;
}
else if(isnagetive and rhs.isnagetive){
if(len == rhs.len){
for(int i = 1; i <= len; i++)
if(num[i] != rhs.num[i]) return num[i] > rhs.num[i];
} else return len > rhs.len;
}
else return isnagetive ? true : false;
}
bool operator > (const Tnum rhs) const {
if(!isnagetive and !rhs.isnagetive){
if(len == rhs.len){
for(int i = 1; i <= len; i++)
if(num[i] != rhs.num[i]) return num[i] > rhs.num[i];
} else return len > rhs.len;
}
else if(isnagetive and rhs.isnagetive){
if(len == rhs.len){
for(int i = 1; i <= len; i++)
if(num[i] != rhs.num[i]) return num[i] < rhs.num[i];
} else return len < rhs.len;
}
else return isnagetive ? false : true;
}
bool operator <= (const Tnum rhs) const { return !(*this > rhs); }
bool operator >= (const Tnum rhs) const { return !(*this < rhs); }
Tnum operator + (Tnum rhs){
Tnum ans;
if(isnagetive and rhs.isnagetive) { ans = add(*this, rhs), ans.isnagetive = 1; return ans; }
if(!isnagetive and !rhs.isnagetive) { ans = add(*this, rhs), ans.isnagetive = 0; return ans; }
if(!isnagetive and rhs.isnagetive){
if(*this > abs(rhs)) ans = minus(*this, rhs);
else ans = minus(rhs, *this), ans.isnagetive = 1;
return ans;
}
if(isnagetive and !rhs.isnagetive){
if(abs(*this) < rhs) ans = minus(rhs, *this);
else ans = minus(*this, rhs), ans.isnagetive = 1;
return ans;
}
}
Tnum operator - (Tnum rhs){
rhs.isnagetive ^= 1;
Tnum ans = *this + rhs;
rhs.isnagetive ^= 1;
return ans;
}
Tnum operator * (Tnum rhs){
Tnum ans;
if(isnagetive == rhs.isnagetive) { ans = mul(*this, rhs); return ans; }
else { ans = mul(*this, rhs), ans.isnagetive = 1; return ans; }
}
void operator += (Tnum rhs) { *this = *this + rhs; }
void operator -= (Tnum rhs) { *this = *this - rhs; }
void operator *= (Tnum rhs) { *this = *this * rhs; }
void print() { if(num[1] and isnagetive) putchar('-'); for(int i = 1; i <= len; i++) cout << num[i]; puts(""); }
#undef MAX_LEN
};
Tnum to(int x){
Tnum ans;
ans.len = log(x) / log(10) + 1;
int pos = 1;
while(x) ans.num[ans.len - pos + 1] = x % 10, x /= 10, pos++;
return ans;
}
59 pts
直接暴力就是 O ( n 4 ) O(n^4) O(n4) ,最后记一下位置然后输出就完了:
namespace sub1{
double s[MAXN][MAXN] = { 0 };
double vv[MAXN][MAXN] = { 0 };
inline double cal(int x, int y, int a, int b) { return s[a][b] - s[x - 1][b] + s[x - 1][y - 1] - s[a][y - 1]; }
void solve(){
double ans = 0; int ax, ay, aa, ab;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) vv[i][j] = log(v[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
s[i][j] = vv[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
for(int x = 1; x < n; x++)
for(int y = 1; y < m; y++)
for(int len1 = 2; x + len1 - 1 <= n; len1++)
for(int len2 = 2; y + len2 - 1 <= m; len2++){
int a = x + len1 - 1, b = y + len2 - 1;
double temp = cal(x, y, a, b) - cal(x + 1, y + 1, a - 1, b - 1);
if(temp > ans) ax = x, ay = y, aa = a, ab = b, ans = temp;
}
Tnum kkk = to(1);
for(int i = ax; i <= aa; i++) kkk *= to(v[i][ay]), kkk *= to(v[i][ab]);
for(int i = ay + 1; i <= ab - 1; i++) kkk *= to(v[ax][i]), kkk *= to(v[aa][i]);
kkk.print();
}
}
100 pts
O ( n 4 ) O(n^4) O(n4) 的复杂度还是太高了,所以我们考虑能不能优化掉一维之类的。
首先我们更换枚举方式,先枚举矩形的上界和下界 a , b a, b a,b,再枚举左界 c c c,注意左界是从 m m m 到 1 1 1 反着枚举的。考虑能不能 O ( 1 ) O(1) O(1) 找到 右界 d d d,首先我们要求的式子就应该是(记录 s x , y , a , b s_{x, y, a, b} sx,y,a,b 右下角为 a , b a, b a,b 左上角为 x , y x, y x,y 的矩形的和):
s a , 1 , a , d + s b , 1 , b , d + s a + 1 , d , b − 1 , d − s a , 1 , a , c − 1 − s b , 1 , b , c − 1 + s a + 1 , c , b − 1 , c s_{a, 1, a, d} + s_{b, 1, b, d} + s_{a + 1, d, b - 1, d} - s_{a, 1, a, c - 1} - s_{b, 1, b, c - 1} + s_{a + 1, c, b - 1, c} sa,1,a,d+sb,1,b,d+sa+1,d,b−1,d−sa,1,a,c−1−sb,1,b,c−1+sa+1,c,b−1,c
为了式子好看一些,我们记 s u m x = s a , 1 , a , x + s b , 1 , b , x , x u m x = s a + 1 , x , b − 1 , x sum_x = s_{a, 1, a, x} + s_{b, 1, b, x}, xum_{x} = s_{a + 1, x, b - 1, x} sumx=sa,1,a,x+sb,1,b,x,xumx=sa+1,x,b−1,x,那么现在式子就变成了这样:
s u m d + x u m d − s u m c − 1 + x u m c sum_d + xum_d - sum_{c - 1} + xum_c sumd+xumd−sumc−1+xumc
然后我们发现这里面和 d d d 有关的式子和 c c c 的位置无关,所以我们考虑倒着扫 c c c 然后记录一个 t e m p temp temp 为当前扫过的部分的 s u m d + x u m d sum_d + xum_d sumd+xumd 的最大值,然后每次 c c c 往左移就比较一下新的位置的 s u m d + x u m d sum_d + xum_d sumd+xumd 和 t e m p temp temp 的大小更新这个最大值就做完了。
namespace sub2{
double s[MAXN][MAXN] = { 0 };
double vv[MAXN][MAXN] = { 0 };
inline double cal(int x, int y, int a, int b) { return s[a][b] - s[x - 1][b] + s[x - 1][y - 1] - s[a][y - 1]; }
inline double xum(int a, int b, int x) { return cal(a + 1, x, b - 1, x); }
inline double sum(int a, int b, int x) { return cal(a, 1, a, x) + cal(b, 1, b, x); }
void solve(){
double ans = 0; int ax, ay, aa, ab;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) vv[i][j] = log(v[i][j]);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
s[i][j] = vv[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
for(int a = 1; a <= n; a++)
for(int b = a + 1; b <= n; b++){
double temp = 0; int d = m;
for(int c = m - 1; c >= 1; c--){
if(temp < sum(a, b, c + 1) + xum(a, b, c + 1))
temp = sum(a, b, c + 1) + xum(a, b, c + 1), d = c + 1;
if(ans < temp - sum(a, b, c - 1) + xum(a, b, c))
ax = a, ay = c, aa = b, ab = d, ans = temp - sum(a, b, c - 1) + xum(a, b, c);
}
}
Tnum kkk = to(1);
for(int i = ax; i <= aa; i++) kkk *= to(v[i][ay]), kkk *= to(v[i][ab]);
for(int i = ay + 1; i <= ab - 1; i++) kkk *= to(v[ax][i]), kkk *= to(v[aa][i]);
kkk.print();
}
}