4250. 【五校联考7day1附加题】路径

Description&Data Constraint

A国有 (n) 个城市,编号为 1 到 (n),任意两个城市之间有一条路。shlw闲得没事干想周游A国,及从城市 1 出发,经过且仅经过除城市 1 外的每个城市 1 次(城市 1 两次),最后回到城市 1。由于shlw很傻,他只愿意走一定长度,多了少了都不干,现在他想知道一共有多少种方案可供选择。

(1le nle14,1le A_{i,j}le10^8,1le lle 2 imes 10^9)

Solution

考虑折半搜索。

哈希记录 ((k,s,sum)) 表示在搜完一半之后,当前点为 (k),点的到达情况为 (s),长度为 (sum)

再次搜索,对于每个搜索判断对应的状态的方案数。

若第二次搜索完的状态为 ((k',s',sum')),则对应的状态是 ((k',2^n-1-s'-1+2^{k-1},l-sum'))

其中 (2^n-1-s'-1+2^{k-1}) 的意思是总状态-当前状态-1+当前点。

Code

#pragma GCC optimize(2)
#include<cstdio>
#define mod 9999997
#define ll long long
using namespace std;
struct hs
{
	int k,s,sum;
	ll tot;
}hash[20000010];
int n,l,dis[20][20];
ll ans,er[20];
bool b[20];
void inhash(ll k,ll s,ll sum)
{
	ll x=k*s%mod*sum%mod;
	while (hash[x].k)
	{
		if (hash[x].k==k&&hash[x].s==s&&hash[x].sum==sum)
		{
			++hash[x].tot;
			return;
		}
		++x;
		if (x>20000000) x=0;
	}
	hash[x].k=k;hash[x].s=s;hash[x].sum=sum;hash[x].tot=1;
}
void find(ll k,ll s,ll sum)
{
	ll x=k*s%mod*sum%mod;
	while (hash[x].k)
	{
		if (hash[x].k==k&&hash[x].s==s&&hash[x].sum==sum)
		{
			ans+=hash[x].tot;
			return;
		}
		++x;
		if (x>20000000) x=0;
	}
}
void dfs1(int now,int x,int s,int sum,int num)
{
	if (x>num)
	{
		inhash(now,s,sum);
		return;
	}
	for (int i=2;i<=n;++i)
		if (!b[i])
		{
			b[i]=true;
			if (sum+dis[now][i]<=l) dfs1(i,x+1,s+er[i-1],sum+dis[now][i],num);
			b[i]=false;
		}
}
void dfs2(int now,int x,int s,int sum,int num)
{
	if (x>num)
	{
		find(now,er[n]-1-s-1+er[now-1],l-sum);
		return;
	}
	for (int i=2;i<=n;++i)
		if (!b[i])
		{
			b[i]=true;
			if (sum+dis[now][i]<=l) dfs2(i,x+1,s+er[i-1],sum+dis[now][i],num);
			b[i]=false;
		}
}
int main()
{
	freopen("way.in","r",stdin);
	freopen("way.out","w",stdout);
	er[0]=1;
	for (int i=1;i<=15;++i)
		er[i]=er[i-1]<<1;
	scanf("%d%d",&n,&l);
	for (int i=1;i<=n;++i)
		for (int j=1;j<=n;++j)
			scanf("%d",&dis[i][j]);
	dfs1(1,1,0,0,n>>1);
	dfs2(1,1,0,0,n-(n>>1));
	printf("%lld
",ans);
	return 0;
} 
原文地址:https://www.cnblogs.com/Livingston/p/15120829.html