两点教训:
- 用 #define 自定义的东西是原模原样的替换过去
在某些情况要记得加括号
#define base (1000*1000*1000)
z.a[i]+= ( (x.a[i]+y.a[i])%base );
z.a[i+1]+=(x.a[i]+y.a[i])/base;
如果不加括号的话,
就会变成
z.a[i]+= ( (x.a[i]+y.a[i])%1000*1000*1000);
z.a[i+1]+=(x.a[i]+y.a[i])/1000*1000*1000;
想要的运算顺序就不对了。
很容易忽视
- 关于输出自己写的高精度
1)直到有非零数才开始输出,前导0不输出,而且本身为零也不输出!!!
2)非零数之后的数才开始输出前导零
3)在满足以上第一条的情况下,又会导致当这个 高精度的数 全部是0 的时候啥也不输出,所以又要特判
void print(ll ans){
bool have=0;
for(int i=3;i>=0;--i){
if(have)
printf("%09d",ans.a[i]);
else if(i==0||ans.a[i])
printf("%d",ans.a[i]);
if(ans.a[i])have=1;
}
printf("\n");
}
这个题要爆long long
高精度
状态转移方程有点反人类
抓住两点
- 找到dp可以表示 我最优就是我最优,而不受其他的影响
(虽然有点傻逼,甚至我自己也突然不知道是啥意思,但是 当我用其他的来表示的时候都不对,然后想到 这个正确的来表示就突然有这种感觉,这一段最优就是这一段最优)
2.由少推多,而不是去想着分解
由少推多 更容易找到解决办法
“当前”由有限的“少”推出来,“当前”不受“多”的影响
dp[i][j]=max(dp[i-1][j]+a[i-1]*x,dp[i][j+1]+a[j+1]*x);
#include<bits/stdc++.h>
#define go(i,a,b) for(int i=a;i<=b;++i)
#define LL long long
#define base (1000*1000*1000)
using namespace std;
struct ll{
LL a[5];
ll(){memset(a,0,sizeof(a));}
};
ll operator + (const ll &x, const ll &y){
ll z;
go(i,0,3){
z.a[i]+= ( (x.a[i]+y.a[i])%base );
z.a[i+1]+=(x.a[i]+y.a[i])/base;
}
return z;
}
ll operator * (const LL &x, const ll &y){
ll z;
go(i,0,3){
z.a[i]+= ( (x*y.a[i])%base );
z.a[i+1]+=(x*y.a[i])/base;
}
return z;
}
void print(ll ans){
bool have=0;
for(int i=3;i>=0;--i){
if(have)
printf("%09d",ans.a[i]);
else if(i==0||ans.a[i])
printf("%d",ans.a[i]);
if(ans.a[i])have=1;
}
printf("\n");
}
LL a[100+20];
ll dp[100+20][100+20];
ll max(ll x,ll y){
for(int i=3;i>=0;--i){
if(x.a[i]>y.a[i])return x;
if(x.a[i]<y.a[i])return y;
}
return x;
}
//for one line
ll f(int m){
go(i,1,m) scanf("%lld",&a[i]);
ll x;
x.a[0]=2;
for(int len=m-1;len>=1;--len,x=2LL*x){
for(int i=1;i+len-1<=m;++i){
int j=i+len-1;
dp[i][j]=max(dp[i-1][j]+a[i-1]*x,dp[i][j+1]+a[j+1]*x);
}
}
ll ans;
go(i,1,m){
ans=max(ans,dp[i][i]+a[i]*x);
}
return ans;
}
void solve(){
int n,m;
ll ans;
cin>>n>>m;
go(i,1,n){
ans=ans+f(m);
}
print(ans);
return;
}
int main(){
int t=1;
//cin>>t;
++t;
while(--t){
solve();
}
return 0;
}