2018.12.20-dtoj-4084-演艺(b)

 

题目描述:

 

一张 N 个点 M 条无向边的图,节点编号为 1 到 N,每条边具有一个正整数的长度。 

 

假定黄花敦会从 S 点出发到达 T 点,并且只会走最短路,wxh 和 wsq 会在 A 点和 B 点 埋伏黄花敦。 

 

为了保证一定能埋伏到黄花敦,同时 wxh 又想制造单独和黄花敦相处的机会,A 点和 B 点必须满足:黄花敦所有可能路径中,必定会经过 A 点和 B 点中的任意一点且不存在一条 路径同时经过 A 点和 B 点。 

 

Wxh 想知道满足上面两个条件的 A,B 点对有多少个,交换 A,B 的顺序算相同的方案。

 

算法:dijk,bitset,拓扑

思路:

首先先建出最短路网,在最短路网中拓扑处理出经过每个点的最短路条数,考虑对于满足条件的AB,经过A的条数与经过B的条数总和等于总最短路条数,且经过两点的条数不重复。

我们有了每个点的最短路条数,考虑怎么把重复的点去掉。重复两点即至少有一条路径能从其中一个点到达另一个点,于是我们跑两遍拓扑,处理出谁能走到我和我能走到谁,即可能重复的点。

然后我们用bitset维护,对于一个点,把权值等于(总最短路树-经过我的最短路数)的bitset和可能重复的点的bitset&起来,即重复的点,再用权值=(总最短路树-经过我的最短路数)的bitset^重复的点数,即可以与我匹配的点的数目,用.count()计算其中1的数目即可。效率是O(n2/32)

以下代码:

#include<bits/stdc++.h>
#define il inline
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=5e4+5,p=998244353;const LL inf=1e18;
int n,m,s,T,head[N],ne[N<<1],to[N<<1],cnt,w[N<<1],tot;LL d[N],ds[N],dt[N],in[N],out[N];int iw[N],ow[N];LL ans;
struct data{int l,r,w;}t[N];queue<int> Q;bitset<N> val[N],k[N],f,g[N];map<int,int> ma;
struct node{int x;LL d;bool operator<(const node&t1)const{return d>t1.d;}};priority_queue<node> q;
il int read(){int x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch^48;_()x=(x<<1)+(x<<3)+(ch^48);return f*x;}
il void insert(int x,int y,int z){ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;}
il void ins(int x,int y){in[y]++;ne[++cnt]=head[x];head[x]=cnt;to[cnt]=y;}
il int mu(int x,int y){if(x+y>=p)return x+y-p;return x+y;}
il void dijk(int S){
    for(int i=1;i<=n;i++)d[i]=inf;d[S]=0;q.push((node){S,d[S]});
    while(!q.empty()){
        node now=q.top();q.pop();int x=now.x;if(now.d!=d[x])continue;
        for(int i=head[x];i;i=ne[i]){
            if(d[to[i]]<=d[x]+w[i])continue;
            d[to[i]]=d[x]+w[i];q.push((node){to[i],d[to[i]]});
        }
    }
}
int main()
{
    n=read();m=read();s=read();T=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),w=read();
        t[i]=(data){x,y,w};insert(x,y,w);insert(y,x,w);
    }
    dijk(s);for(int i=1;i<=n;i++)ds[i]=d[i];
    dijk(T);for(int i=1;i<=n;i++)dt[i]=d[i];
    for(int i=1;i<=n;i++)head[i]=0;cnt=0;
    for(int i=1;i<=m;i++){
        int x=t[i].l,y=t[i].r;
        if(ds[x]+dt[y]+t[i].w==ds[T])ins(x,y);
        if(dt[x]+ds[y]+t[i].w==ds[T])ins(y,x);
    }
    Q.push(s);iw[s]=1;
    while(!Q.empty()){
        int x=Q.front();Q.pop();k[x][x]=1;
        for(int i=head[x];i;i=ne[i]){
            iw[to[i]]=mu(iw[to[i]],iw[x]);k[to[i]]|=k[x];
            if(--in[to[i]]==0)Q.push(to[i]);
        }
    }
    for(int i=1;i<=n;i++)head[i]=0,in[i]=0;cnt=0;
    for(int i=1;i<=m;i++){
        int x=t[i].l,y=t[i].r;
        if(ds[x]+dt[y]+t[i].w==ds[T])ins(y,x);
        if(dt[x]+ds[y]+t[i].w==ds[T])ins(x,y);
    }
    Q.push(T);ow[T]=1;
    while(!Q.empty()){
        int x=Q.front();Q.pop();g[x][x]=1;
        for(int i=head[x];i;i=ne[i]){
            ow[to[i]]=mu(ow[to[i]],ow[x]);g[to[i]]|=g[x];
            if(--in[to[i]]==0)Q.push(to[i]);
        }
    }
    for(int i=1;i<=n;i++){
        iw[i]=1ll*iw[i]*ow[i]%p;
        if(!ma[iw[i]])ma[iw[i]]=++tot;
        val[ma[iw[i]]][i]=1;k[i][i]=1;
    }
    int sum=iw[T];
    for(int i=1;i<=n;i++){
        int R=ma[mu((sum-iw[i])%p,p)];
        f=val[R]&(k[i]|g[i]);
        f=val[R]^f;ans+=f.count();
    }
    printf("%lld
",ans>>1ll);
    return 0;
}
View Code

//全场最慢...

原文地址:https://www.cnblogs.com/Jessie-/p/10153614.html