传送门
题解:
非常显然的轮廓线DP。压轮廓线上的连通性就可以了。
连通性还是用最小表示法,但是每次重排常数太大,需要记忆一下。
但是常数还是很大,主要在 if else 上面,太多选择情况导致分支预测的惩罚非常高。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
namespace IO{
inline char gc(){
static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}template<typename T>T get_integer(){
char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}
}using namespace IO;
using std::cerr;
using std::cout;
#define fi first
#define se second
cs int mod=1e9+7;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
cs int N=3e4+7;
struct atom{ll vl;int ct;atom(){vl=1e18,ct=0;}atom(ll _v,int _c):vl(_v),ct(_c){}};
atom& operator+=(atom &a,cs atom &b){
return a=(a.vl!=b.vl)?(a.vl<b.vl?a:b):atom(a.vl,add(a.ct,b.ct));
}
atom operator+(cs atom &a,cs ll &b){
return atom(a.vl+b,a.ct);
}
cs int SIZE=1<<18|7;
struct Map{
std::pair<atom,bool> vl[SIZE];
int st[SIZE],tp;Map(){}
void clr(){
for(int re i=1;i<=tp;++i)
vl[st[i]].se=false;tp=0;
}atom &operator[](int k){
if(!vl[k].se){
vl[k]={atom(),true};
st[++tp]=k;
}return vl[k].fi;
}bool find(int k)cs{return vl[k].se;}
}dp[2];
int nw,pr=1;
int n,m,up[N][10],le[N][10];
inline int get(int s,int t){return s>>(t*3)&7;}
inline int set(int s,int t,int st){return s^((get(s,t)^st)<<(t*3));}
int ct[SIZE][10];
void other(int s,int st){
for(int re i=0;i<m;++i)
if(get(s,i)==st&&++ct[s][st]>1)
return ;
}
int restate[SIZE],vs[SIZE];
void relable(int s){
if(vs[s])return ;
int ct=0,tmp=s;static int id[8];
memset(id,-1,sizeof id);
for(int re i=0;i<m;++i){
int t=get(s,i);
s=set(s,i,id[t]!=-1?id[t]:id[t]=ct++);
}vs[tmp]=true;restate[tmp]=s;
}
int tr[SIZE][7];
void mg(int s,int j){
int &res=tr[s][j];
int t1=get(s,j-1),t2=get(s,j);
if(t1==t2)return (void)(res=s);
for(int re p=0;p<m;++p)
if(get(s,p)==t2)s=set(s,p,t1);
res=s;
}
void calc(int s){
static bool work[SIZE];
if(work[s])return ;work[s]=true;relable(s);
for(int re j=0;j<m;++j)relable(set(s,j,7));
for(int re j=1;j<m;++j)mg(s,j),relable(tr[s][j]);
for(int re j=0;j<m;++j)other(s,j);
for(int re j=1;j<m;++j)relable(set(s,j,get(s,j-1)));
}
void update(int s,cs atom &vl){
dp[nw][restate[s]]+=vl;
}
void init_dp(){
for(int re st=0;st<(1<<(m-1));++st){
int s=0,ct=0;ll vl=0;
for(int re t=1;t<m;++t){
if(st&(1<<(t-1)))
vl+=le[0][t];
else ++ct;
s|=ct<<(t*3);
}dp[nw][s]=atom(vl,1);
}
}
void Main(){
n=gi(),m=gi();
for(int re i=0;i<n;++i)
for(int re j=1;j<m;++j)
le[i][j]=gi();
for(int re i=0;i<m;++i)
for(int re j=1;j<n;++j)
up[j][i]=gi();
init_dp();
for(int re i=1;i<n;++i)
for(int re j=0;j<m;++j){
nw^=1,pr^=1;dp[nw].clr();
for(int re t=1;t<=dp[pr].tp;++t){
int s=dp[pr].st[t];
atom vl=dp[pr][s];calc(s);
if(j==0){
int t1=get(s,j);
update(s,vl+up[i][j]);
if(ct[s][t1]>1)
update(set(s,j,7),vl);
}else {
int t1=get(s,j-1),t2=get(s,j);
if(ct[s][t2]>1){
update(set(s,j,t1),vl+le[i][j]);
update(set(s,j,7),vl);
}
update(s,vl+up[i][j]);
if(t1!=t2){
vl.vl+=up[i][j]+le[i][j];
update(tr[s][j],vl);
}
}
}
}
cout<<dp[nw][0].ct<<"\n";
}
inline void file(){
#ifdef zxyoi
freopen("c.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}