【JZOJ 6524】Compound Escape

题目

Description
Bessie和她的朋友们被抓走并关在了远离农场的一个秘密房屋,现在该是Bessie站出来策划脱逃的时候了!这一房屋包含NK个排列成N×K矩形方阵的囚室,水平和垂直方向相邻的囚室之间有门互通。每个囚室中有一头奶牛。

Bessie黑进了系统,可以解锁任意一部分的门,但是每个门均有其代价。为了使奶牛们能够脱逃,Bessie必须打开足够多的门使得所有的奶牛可以聚集在一个房间内(这样她们就能拥有足够的力量挖地洞通向外面!)。Bessie想要使得总的解锁花费最小。

但是这次行动异常关键,Bessie不能满足于仅仅一个脱逃方案:她还需要后备方案。帮助她计算最小代价脱逃方案的数量;如果某一扇门在一个方案中被解锁了而在另一个方案中没有,那么这两个方案就被认为是不同的。

由于这个数字可能非常大,只需输出该数对10^9+7取模的余数。

Input
输入的第一行包含两个空格分隔的整数N和K(2≤N≤30000,2≤K≤6)。以下N行每行包含K−1个空格分隔的整数,为解锁一条水平边上的每扇门需要花费的代价。

以下K行每行包含N−1个空格分隔的整数,为解锁一条垂直边上的每扇门需要花费的代价。

所有的花费均在1到10^9之间。

对于20%的测试数据,保证N≤500,所有的代价均在1到5之间。

对于另外20%的测试数据,保证N≤5000。

Output
一个整数:最小花费的逃脱方案的数量,对10^9+7取模。

Sample Input
4 3
1 1
5 6
7 8
1 1
1 1 1
2 3 4
1 1 1

Sample Output
10
这个测试样例描述了一个4x3的方阵,
1 1
±----±----+
| | |
1 | |2 | 1
| 5 | 6 |
±----±----+
| | |
1 | |3 | 1
| 7 | 8 |
±----±----+
| | |
1 | |4 | 1
| | |
±----±----+
1 1
所有的最小代价脱逃方案都会使用代价为2的门,代价为3的门,以及某九个代价为1的门。由于有十种方式选择不被使用的代价为1的门,所以答案为10。

Data Constraint

思路

轮廓线DP
最小表示法压连通性

把每次转移的预处理出来
每次可以直接枚举2k横边情况和2k-1竖边情况转移

代码

#pragma GCC optimize (2)
#include<bits/stdc++.h>
#define ll long long
using namespace std; 
const int N=3e4+77,mo=1e9+7; 
int n,k,R[N][10],D[N][10],o,tot; 
ll f[2][140]; 
int g[2][140],a[10],b[10],h[10000000],w[10]; 
int q[140][10],sg[140][10],v[140],sep; 
int yjy[140][10][2][2]; 
void add(int &x, int y)
{
	x+=y; if(x>=mo)x-=mo; 
}
void dfs(int x,int s)
{
	if(x>k)
	{
		sep=h[s]=++tot; 
		v[tot]=s; 
		memcpy(q[tot],a,sizeof a); 
		for(int i=1; i<=k; i++)sg[tot][i]=(b[i]==1&&a[i]==i); 
		return; 
	}
	int le=n; 
	for(int i=x-1; ~i; i--)
	{
		if(i>le)i=le; 
		if(a[i]==i)
		{
			if(i==0)b[x]=1,a[x]=x; 
			else 
			{	
				a[x]=i; 
				b[i]++; 
			}
			dfs(x+1,s+a[x]*w[x]); 
			if(i!=0)b[i]--; 
			a[x]=0; 
		}
		le=min(le,a[i]); 
	}
}

void init()
{
	for(int y=1; y<=k; y++)
	for(int s=1; s<=tot; s++)
	{
		for(int up=0; up<2; up++)
		{
			for(int le=0; le<(y==1?1:2); le++)
			{
				int ns=v[s]; 
				if(up+le==0)
				{
					ns+=(y-q[s][y])*w[y]; 
					if(q[s][y]==y)
					{
						int mi=k+1; 
						for(int i=y+1; i<=k; i++)if(q[s][i]==y)
						{
							mi=min(mi,i); 
						}
						for(int i=y+1; i<=k; i++)if(q[s][i]==y)
						{
							ns+=(mi-q[s][i])*w[i]; 
						}
					}
				}
				else if(up+le==2)
				{
					int u=q[s][y],v=q[s][y-1]; 
					int z=min(u,v); 
					for(int i=1; i<=k; i++)if(q[s][i]==u||q[s][i]==v)
					{
						ns+=(z-q[s][i])*w[i]; 
					}
				}
				else if(up==1)
				{
				}
				else if(le==1)
				{
					ns+=(q[s][y-1]-q[s][y])*w[y]; 
					if(q[s][y]==y)
					{
						int mi=k+1; 
						for(int i=y+1; i<=k; i++)if(q[s][i]==y)
						{
							mi=min(mi,i); 
						}
						for(int i=y+1; i<=k; i++)if(q[s][i]==y)
						{
							ns+=(mi-q[s][i])*w[i]; 
						}
					}
				}
				yjy[s][y][up][le]=h[ns]; 
			}
		}
	}
}
int main() 
{
	freopen("escape.in","r",stdin); freopen("escape.out","w",stdout); 
	scanf("%d%d",&n,&k);
	w[1]=1;
	for(int i=2; i<=k; i++) w[i]=w[i-1]*10; 
	for(int i=1; i<=n; i++)
		for(int j=1; j<k; j++) scanf("%d",&R[i][j]); 
	for(int j=1; j<=k; j++)
		for(int i=1; i<n; i++) scanf("%d",&D[i][j]); 
	dfs(1,0); 
	init(); 
	memset(f,127,sizeof f); 
	f[o][sep]=0,g[o][sep]=1; 
	for(int x=1; x<=n; x++)
	{
		for(int y=1; y<=k; y++)
		{
			o^=1; 
			memset(f[o],127,sizeof f[o]); 
			memset(g[o],0,sizeof g[o]); 
			for(int s=1; s<=tot; s++)if(g[o^1][s])
			{
				for(int up=0; up<(x==1?1:2); up++)
				{
					if(up==0&&x!=1&&sg[s][y]) continue; 
					for(int le=0; le<(y==1?1:2); le++)
					{
						int ns=yjy[s][y][up][le]; 
						if(ns==0) continue; 
						ll nf=f[o^1][s]+le*R[x][y-1]+up*D[x-1][y]; 
						if(f[o][ns]==nf)
						{
							add(g[o][ns],g[o^1][s]); 
						} 
						else if(f[o][ns]>nf)
						{
							f[o][ns]=nf; 
							g[o][ns]=g[o^1][s]; 
						}
					}
				}
			}
		}
	}
	printf("%d",g[o][1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值