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)
以下代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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\n",ans>>1ll); return 0; }
//全场最慢...