4250. 【五校联考7day1附加题】路径
Description&Data Constraint
A国有 \(n\) 个城市,编号为 1 到 \(n\),任意两个城市之间有一条路。shlw闲得没事干想周游A国,及从城市 1 出发,经过且仅经过除城市 1 外的每个城市 1 次(城市 1 两次),最后回到城市 1。由于shlw很傻,他只愿意走一定长度,多了少了都不干,现在他想知道一共有多少种方案可供选择。
\(1\le n\le14,1\le A_{i,j}\le10^8,1\le l\le 2\times 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\n",ans);
return 0;
}