10.27练习题 托马斯小火车
10.27练习题 托马斯小火车
KONO题面哒!
问题描述
随着全 损 音 质的托马斯主题曲响起,托马斯小火车开始飙车啦!托马斯所在的城镇可看做一个由n个点m条边构成的无向图,每条边都有一定的长度。图中有两辆自带全 损 音 质主题曲的托马斯甲和托马斯乙。甲从A点沿最短路走向B点,乙从B点沿最短路走向A点。 甲乙两辆托马斯之间存在无法预测的量子纠缠,他们路途中相遇就会飙歌。
问,有多少种走法,城镇中的居民不会受到全 损 音 质的噪音污染。 相遇是指同在某个点或者某条边上相遇。 答案可能很大,mod 10^9+7后再输出。
输入格式
第一行,两个整数n,m 第二行,两个整数A,B 接下来m行,每行三个整数x,y,z,表示点x与点y间有一条长z的边。
输出格式
一个整数,表示方案数。
样例输入1
4 4
1 3
1 2 1
2 3 1
3 4 1
4 1 1
样例输出1
2
样例输入2
8 13
4 2
7 3 9
6 2 3
1 6 4
7 6 9
3 8 9
1 2 2
2 8 12
8 6 9
2 5 5
4 2 18
5 3 7
5 1 515371567
4 8 6
样例输入2
6
数据范围
对于 100% 的数据,1 ≤ n ≤100000 1 ≤ m ≤200000 1≤z≤10^9 图连通,且没有重边
一直在挣扎A,C。看了(其实是听了)之后才发现这是道大水题)
考察点:最短路 容斥
两次 \(dijkstra\) 求出 A 到 B 和 B 到 A 的最短路,设最短路长度为\(LenAB\)
在进行 \(dijkstra\) 的过程中,记录下列信息:
从 A 到点 i 的最短距离记为 \(DisA[i]\),从 A 到 i 的最短路线数量\(CntA[i]\)
从 B 到点 i 的最短距离记为 \(DisB[i]\),从 B 到 i 的最短路线数量\(CntB[i]\)
考虑用容斥原理求解答案:总方案数-不合法方案数
总方案数 \(Tot=CntA[B]*CntB[A]\)
不合法方案:
1.若在 i 号点相遇:\(DisA[i]+DisB[i]=LenAB\) 且 \(DisA[i]==DisB[i]\)
方案数=\(CntA[i]*CntB[i]*CntB[i]*CntA[i]=(CntA[i]*CntB[i])^2\)
\(CntA[i]*CntB[i]\)只是表示走到相汇点 i 的方案数。剩下的路还要继续走,所以还要乘以\(CntA[i]*CntB[i]\)
2.若在 i 号边中相遇,设边的两个端点为 x,y:
\(DisA[x]+Len[i]+DisB[y]=LenAB\) 且 \(DisA[x]+Len[i]>DisB[y]\) 且 \(DisB[y]+Len[i]>DisA[x]\)
方案数=\((CntA[x]*CntB[y])^2\)
KONO代码哒!
#include<cmath>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
struct edge{
int from,to,next,dis;
}g[400010];
typedef long long ll;
const ll maxn=100010;
const ll inf=0x3f3f3f3f;
const ll mod=1e9+7;
ll head[maxn],cnt;
ll n,m,A,B;
ll num[2][maxn],dis[2][maxn];
void add(ll from,ll to,ll dis)
{
g[++cnt].next=head[from];
g[cnt].to=to;
g[cnt].dis=dis;
g[cnt].from=from;
head[from]=cnt;
}
struct node{
ll to,dis;
};
bool operator<(node a,node b)
{
return a.dis>b.dis;
}
priority_queue<node>q;
bool mark[maxn];
void dij(ll x,ll pos)
{
memset(mark,0,sizeof(mark));
dis[pos][x]=0;
num[pos][x]=1;
q.push((node){x,0});
while(q.size())
{
ll u=q.top().to;
q.pop();
if(mark[u])continue;
mark[u]=1;
for(ll i=head[u];i;i=g[i].next){
ll v=g[i].to,d=g[i].dis;
if(!mark[v]&&dis[pos][u]+d<dis[pos][v]){
num[pos][v]=num[pos][u];
dis[pos][v]=dis[pos][u]+d;
q.push((node){v,dis[pos][v]});
}
else if(dis[pos][u]+d==dis[pos][v]){
num[pos][v]=(num[pos][v]+num[pos][u])%mod;
}
}
}
}
ll pow(ll x)
{
return (x%mod)*(x%mod)%mod;
}
ll reduce(ll a,ll b)
{
return ((a-b)%mod+mod)%mod;
}
int main()
{
ios::sync_with_stdio(0);
cin>>n>>m>>A>>B;
ll i,j;
for(i=1;i<=m;i++){
ll x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
memset(dis,0x3f,sizeof(dis));
// for(i=1;i<=n;i++)num[0][i]=num[1][i]=1;
dij(A,0);
dij(B,1);
// for(i=0;i<=1;i++)
// {
// for(j=1;j<=n;j++)
// {
// printf("%lld %lld\n",dis[i][j],num[i][j]);
// }
// }
ll len=dis[0][B];
ll ans=(num[0][B]*num[1][A])%mod;
// printf("%lld\n",ans);
for(i=1;i<=n;i++)
{
if(dis[0][i]+dis[1][i]==len&&dis[0][i]==dis[1][i]){
ans=reduce(ans,pow(num[0][i]*num[1][i]));
}
}
// printf("%lld\n",ans);
for(i=1;i<=cnt;i++)
{
int f=g[i].from,t=g[i].to,d=g[i].dis;
if(dis[0][f]+d+dis[1][t]==len&&dis[0][f]+d>dis[1][t]&&dis[1][t]+d>dis[0][f])
{
ans=reduce(ans,pow(num[0][f]*num[1][t]));
}
}
printf("%lld\n",ans);
}
(直接贴的solution,先去搞一搞服务器的事,之后再用沙雕语言详细写)


浙公网安备 33010602011771号