T7025 miaom与+1(一)

以下摘自王哲凡教授的课件。
先构造出 S T 的最短路网。
最短路网是啥?
满足 dis_(S,x)+W_(x,y)+dis_(y,T)=dis_(S,T) 的边 ( x,y ) 组成的图。
然后在最短路网上 dp S 到每个点 x 的路径条数 P_x 和 每个点 x T 的路径条数 Q_x
P_T=Q_S=Z
一个点 x 为必经点当且仅当 Z=P_x Q_x
一条边 ( x,y ) 为必经边当且仅当 Z=P_x Q_y
路径数太大爆 long long 怎么办?
随便找个好看的数取模就好了嘛。
时间复杂度 O((n+m)  logm) 大概
下面是我写的代码。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<algorithm>
#include<set>
#include<bitset>
#include<queue>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define G getchar()
#define LL long long
#define pii pair<int,int>
#define mkp make_pair
#define X first
#define Y second
#define N 100005
#define NN 300005
#define inf=300000000
priority_queue<pii,vector<pii>,greater<pii> >pq;
int n,m,s,t;struct EDGE{int a,b,w;}e[NN];
int rhe[N],he[N],ne[NN<<1],to[NN<<1],W[NN<<1],tot;
int d[N],rd[N],md;bool vis[N];
int q[N],in[N],rin[N],f[N],rf[N],sum;
int ans[N],cnt;
void read(int &x){
	char ch=G;
	while(ch<48||ch>57)ch=G;
	for(x=0;ch>47&&ch<58;ch=G)x=x*10+ch-48;
}
void add(int x,int y,int z){
	to[++tot]=y;W[tot]=z;ne[tot]=he[x];he[x]=tot;
	to[++tot]=x;W[tot]=z;ne[tot]=rhe[y];rhe[y]=tot;
}
void dijkstra(){
	int i,x,y,tmp;
	memset(d,63,sizeof d);
	pq.push(mkp(d[s]=0,s));
	while(!pq.empty()){
		x=pq.top().Y;pq.pop();
		if(vis[x])continue;vis[x]=1;
		for(i=he[x];i;i=ne[i])
			if((tmp=d[x]+W[i])<d[y=to[i]])
				pq.push(mkp(d[y]=tmp,y));
	}
	md=d[t];
}
void rdijkstra(){
	memset(vis,0,sizeof vis);
	int i,x,y,tmp;
	memset(rd,63,sizeof rd);
	pq.push(mkp(rd[t]=0,t));
	while(!pq.empty()){
		x=pq.top().Y;pq.pop();
		if(vis[x])continue;vis[x]=1;
		for(i=rhe[x];i;i=ne[i])
			if((tmp=rd[x]+W[i])<rd[y=to[i]])
				pq.push(mkp(rd[y]=tmp,y));
	}
}
void dp(){
	int Ft=1,Rr=2,u,v,i;
	for(f[q[1]=s]=1;Ft<Rr;){
		for(i=he[u=q[Ft++]];i;i=ne[i]){
			f[v=to[i]]+=f[u];
			if(!(--in[v]))q[Rr++]=v;
		}
	}
	sum=f[t];
}
void rdp(){
	int Ft=1,Rr=2,u,v,i;
	for(rf[q[1]=t]=1;Ft<Rr;){
		for(i=rhe[u=q[Ft++]];i;i=ne[i]){
			rf[v=to[i]]+=rf[u];
			if(!(--rin[v]))q[Rr++]=v;
		}
	}
}
int main(){
	int i;
	read(n);read(m);read(s);read(t);
	rep(i,1,m){
		read(e[i].a);read(e[i].b);read(e[i].w);
		add(e[i].a,e[i].b,e[i].w);
	}
	dijkstra();rdijkstra();
	memset(he,0,sizeof he);memset(rhe,0,sizeof rhe);tot=0;
	rep(i,1,m)if(d[e[i].a]+e[i].w+rd[e[i].b]==md)
		add(e[i].a,e[i].b,e[i].w),++in[e[i].b],++rin[e[i].a];
	dp();rdp();
	rep(i,1,n)if(f[i]*rf[i]==sum)ans[++cnt]=i;
	printf("%d\n",cnt);
	sort(ans+1,ans+1+cnt);
	rep(i,1,cnt)printf("%d ",ans[i]);puts("");
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值