bzoj5109(图论好题)

我的参考题解:https://www.cnblogs.com/ccz181078/p/7907022.html;

不过我感觉题解的压位有问题,(1<<x)还不炸上天。不过这题数据水,好像怎么写都能对,这里放上我认为正确的写法

//正经解法是计算一个F(x)表示经过x的最短路条数,然后找F(A)+F(B)=F(T),用map来做。
//而且F(x)可能很大,还要取模,一个不够的话可能还要取两个。
//而网上的这种随机算法相比之下就显得很简洁了(但由于这题数据很弱好像这部分写错也可以过)
//关于这种做法的正确性,显然所有的最短路都经过的点的f值为0
//那么为什么靠两个f值相同且非0且不能在树形图中互达的点能将图分成两个部分呢,我们可以考虑用一个点将两部分连起来
//这样这个点的f值为0,也就是说它能到所有点,那把它去掉图当然被分成两个部分。
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long i64;
typedef unsigned long long u64;
typedef unsigned int u32;
const int N=5e4+5;
int n,m,pp,cp,fa[N],S,T,last[N],pre[N*2],other[N*2],w[N*2],t;
void add(int x,int y,int z){
    ++t;pre[t]=last[x];last[x]=t;other[t]=y;w[t]=z;
}
struct node{
    int id;
    i64 dis;
    bool operator<(const node&a)const{
        return dis>a.dis;
    }
}A,B;
priority_queue<node>q;
bool vis[N];
i64 dis[N],ans=0;
u64 f[N];
u32 can[N][N/32+5];
int _get(u32*a,int x){return (a[x>>5]>>(x&31))&1;}
void _set(u32*a,int x){a[x>>5]|=1<<(x&31);}
void _or(u32*a,u32*b){
    int p=n/32+1;
    for(int i=0;i<=p;i+=4){
        a[i]|=b[i];a[i+1]|=b[i+1];
        a[i+2]|=b[i+2];a[i+3]|=b[i+3];
    }
}
void f1(int x){//从T往回找最短路图,我觉得和从S开始找是一样的。
    vis[x]=1;
    for(int i=last[x];i;i=pre[i]){
        int v=other[i];
        if(dis[v]+w[i]==dis[x]){
            if(!vis[v])fa[v]=x,f1(v);
            else{
                u64 z=rand();
                z=z<<31^rand();
                f[x]^=z;f[fa[v]]^=z;
            }
        }
    }
}
struct hehe{
    int id;
    u64 v,dis;
    bool operator<(const hehe&a)const{
        return v!=a.v?v<a.v:dis>a.dis;
    }
}tmp[N];
void f2(int x){
    _set(can[x],x);
    vis[x]=0;
    for(int i=last[x];i;i=pre[i]){
        int v=other[i];
        if(dis[v]+w[i]==dis[x]){
            if(vis[v])f2(v),f[x]^=f[v];
            _or(can[x],can[v]);
        }
    }
    if(f[x])tmp[pp++]=(hehe){x,f[x],dis[x]};
    else ++cp;
}
void calc(){
    f1(T);f2(T);
    sort(tmp,tmp+pp);
    for(int i=0,j=0;i<pp;i=j){
        int t=1;
        for(++j;j<pp&&tmp[i].v==tmp[j].v;++j)t+=_get(can[tmp[i].id],tmp[j].id);
        //图一定可以分成两个部分,一部分是经过A的最短路,一部分是经过B的,两部分没有交集。
        ans+=i64(j-i-t)*t;//因为f值相同的是按dis值排序的,所以此时的f一定是按A,B分的两个集合中的一个。
    }
    ans+=cp*i64(n-cp-pp);
    printf("%lld
",ans);
    //system("pause");
}
bool dijkstra(){
    for(int i=0;i<=n;++i)dis[i]=1ll<<60;
    A.id=S;A.dis=0;dis[S]=0;q.push(A);
    while(!q.empty()){
        B=q.top();q.pop();
        if(B.dis!=dis[B.id])continue;
        if(B.id==T){calc();return 1;}
        for(int i=last[B.id];i;i=pre[i]){
            int v=other[i];
            if(dis[v]>dis[B.id]+w[i]){
                dis[v]=dis[B.id]+w[i];
                A.id=v;A.dis=dis[v];q.push(A);
            }
        }
    }
    return 0;
}
int main(){
    int x,y,z;
    cin>>n>>m>>S>>T;
    srand(n^m^T^S^12341);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);add(y,x,z);
    }
    if(!dijkstra())printf("%lld
",n*i64(n-1)/2);
    //system("pause");
    return 0;
}
原文地址:https://www.cnblogs.com/dibaotianxing/p/8335885.html