题目链接 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);
}