黑暗城堡

题目链接  https://ajax.loj.ac/problem/10064

题意:给你n个点,m条边,任意选边连接,保证连边后一号点到任意点的最短路和m条边全部连接后一号点到该点的最短路相同即为一种合法方案。统计方案种数并对2的31次-1取模。

题解:这题,实在是比较玄学。我们先跑一边dijkstra,求出1号点到任意点的最短路。然后我们把dis值从小到大排序,然后运用prim的思想,进行枚举,不断将节点加入T集合,如果p节点,满足dis[p]=dis[x]+v[x][p](v为边的长度,x在T集合内),那么当前步骤的方案数+1,最后通过乘法原理,得到了结果。
 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define pr pair < int , int >
using namespace std;
priority_queue< pr, vector< pr > , greater< pr > > q;
const long long mo=(1LL<<31)-1;
int cnt,n,m,head[2000000],nxt[2000000],to[2000000],val[2000000],p[2000000],vis[2000000],dis[1000005];
bool f[2000000];
struct xy{
	int dis,id;
}e[2000000];
bool cmp(xy x,xy y)
{
	return x.dis<y.dis;
}
void add(int x,int y,int z)
{
	cnt++;nxt[cnt]=head[x];to[cnt]=y;val[cnt]=z;head[x]=cnt;
}
void dijkstra()
{
	for (int i=1;i<=n;i++) e[i].dis=1000000000;
	memset(vis,0,sizeof(vis));
	e[1].dis=0;
	q.push(make_pair(0,1));
	while (q.size())
	{
		int x=q.top().second;q.pop();
		if (vis[x]) continue;
		vis[x]=1;
		for (int i=head[x];i;i=nxt[i])
		{
			int y=to[i],z=val[i];
			if (e[y].dis>e[x].dis+z) {e[y].dis=e[x].dis+z;q.push(make_pair(e[y].dis,y));}
		}
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int x,y,z;
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	dijkstra();
	for (int i=1;i<=n;i++) e[i].id=i,dis[i]=e[i].dis;
	sort(e+1,e+n+1,cmp);
	memset(f,false,sizeof(f));
	f[1]=true;
	for (int i=2;i<=n;i++)
	{
		f[e[i].id]=true;
		for (int j=head[e[i].id];j;j=nxt[j])
		{
			y=to[j];
			if (f[y] && dis[y]+val[j]==e[i].dis) p[i]++;
		}
	}
	long long ans=1;
	for (int i=2;i<=n;i++)
		ans=(ans*p[i])%mo;
	printf("%lld\n",ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值