【USACO19OPEN】Compound Escape P(轮廓线DP)

传送门


题解:

非常显然的轮廓线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;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值