【最短路+容斥】arc090E Avoiding Collision

题目:https://vjudge.net/contest/464115#problem/G
题意:

一个 n n n个点 m m m条边的无向图,每条边有一个边权 w w w。有一个起点 s s s和终点 t t t,求从 s s s出发到 t t t和从 t t t出发到 s s s的不相遇的两条最短路径的路径对数。

思路:

d i j k s t r a dijkstra dijkstra 求出起点到 i i i的距离 d i s [ 0 ] [ i ] dis[0][i] dis[0][i],终点到 i i i的距离 d i s [ 1 ] [ i ] dis[1][i] dis[1][i],起点到 i i i最短路的方案数 c n t [ 0 ] [ i ] cnt[0][i] cnt[0][i],终点到 i i i的最短路的方案数 c n t [ 1 ] [ i ] cnt[1][i] cnt[1][i]

求出所有最短路径数,任选两条 C c n t [ 0 ] [ t ] 2 ∗ 2 C_{cnt[0][t]}^2*2 Ccnt[0][t]22的方案数减去相遇的方案数就是答案

考虑在点上相遇

d i s [ 0 ] [ i ] = = d i s [ 1 ] [ i ] dis[0][i]==dis[1][i] dis[0][i]==dis[1][i]&& d i s [ 0 ] [ i ] = = 2 ∗ d i s [ 0 ] [ t ] dis[0][i]==2*dis[0][t] dis[0][i]==2dis[0][t]时,在点 i i i相遇的方案为 C c n t [ 0 ] [ i ] ∗ c n t [ 1 ] [ i ] 2 ∗ 2 C_{cnt[0][i]*cnt[1][i]}^2*2 Ccnt[0][i]cnt[1][i]22

考虑在边上相遇

设边 i i i的端点 u i u_i ui v i v_i vi u i u_i ui理起点更近,边权为 w i w_i wi

d i s [ 0 ] [ u ] + d i s [ 1 ] [ v ] + w = = d i s [ 0 ] [ t ] dis[0][u]+dis[1][v]+w==dis[0][t] dis[0][u]+dis[1][v]+w==dis[0][t]&& d i s [ 0 ] [ u ] ∗ 2 < d i s [ 0 ] [ t ] dis[0][u]*2<dis[0][t] dis[0][u]2<dis[0][t]&& d i s [ 1 ] [ v ] ∗ 2 < d i s [ 0 ] [ t ] dis[1][v]*2<dis[0][t] dis[1][v]2<dis[0][t]时,在边 i i i相遇的方案数为 C c n t [ 0 ] [ u ] ∗ c n t [ 1 ] [ v ] 2 ∗ 2 C_{cnt[0][u]*cnt[1][v]}^2*2 Ccnt[0][u]cnt[1][v]22

代码:
#include<bits/stdc++.h>
#define MAXN 100005
#define INF 0x3f3f3f3f
#define pb push_back

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,int>P;
vector<P>G[MAXN];
const int mod=1e9+7;

struct Edge
{
    int u,v,w;
}edge[MAXN*2];

ll dis[2][MAXN],cnt[2][MAXN];
int vis[MAXN];
void dijkstra(int id,int s)
{
    memset(dis[id],60,sizeof(dis[id]));
    memset(vis,0,sizeof(vis));
    dis[id][s]=0;
    cnt[id][s]=1;
    priority_queue<P,vector<P>,greater<P> >q;
    q.push(P(0,s));
    while(!q.empty())
    {
        P p=q.top();q.pop();
        int x=p.second;
        if(vis[x])continue;
        vis[x]=1;
        for(P e:G[x])
        {
            int to=e.second;//cout<<x<<' '<<to<<' '<<dis[id][to]<<endl;
            ll w=e.first;
            if(dis[id][to]>dis[id][x]+w)
            {
                dis[id][to]=dis[id][x]+w;
                cnt[id][to]=cnt[id][x];
                q.push(P(dis[id][to],to));
            }
            else if(dis[id][to]==dis[id][x]+w)
                cnt[id][to]=(cnt[id][to]+cnt[id][x])%mod;
        }
    }
}

int main(){
    int n,m;scanf("%d%d",&n,&m);
    int s,t;scanf("%d%d",&s,&t);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;scanf("%d%d%d",&u,&v,&w);
        edge[i]={u,v,w};
        G[u].pb(P(w,v));
        G[v].pb(P(w,u));
    }
    dijkstra(0,s);
    dijkstra(1,t);
    ll ans=cnt[0][t]*(cnt[0][t]-1)%mod;//cout<<ans<<endl;
    for(int i=1;i<=n;i++)
    {
        if(dis[0][i]==dis[1][i]&&dis[0][i]*2==dis[0][t])
        {
            ll tmp=cnt[0][i]*cnt[1][i]%mod;
            ans=(ans-tmp*(tmp-1)%mod+mod)%mod;
        }
    }
    for(int i=1;i<=m;i++)
    {
        int u=edge[i].u,v=edge[i].v,w=edge[i].w;
        if(dis[0][u]>dis[0][v])swap(u,v);
        if(dis[0][u]+dis[1][v]+w==dis[0][t]&&dis[0][u]*2<dis[0][t]&&dis[1][v]*2<dis[0][t])
        {
            ll tmp=cnt[0][u]*cnt[1][v]%mod;
            ans=(ans-tmp*(tmp-1)%mod+mod)%mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值