Codeforces Round #Pi (Div. 2) E

本文介绍了一种判断有向图中每条边是否位于唯一最短路径上的方法。通过两次Dijkstra算法求出起点到终点的所有最短路径及其数量,再利用Tarjan算法寻找这些路径中的关键边(桥)。适用于解决图论问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        一个有向图,s为起点,t为终点,问每条边是否在唯一的最短路上。

        对s和t分别求一次单源最短路(dijkstra),顺便记录方案数。然后对每条边检查,长度符合的话,就说明在最短路上,如果方案数也符合,就是唯一的。这个方案数可能很大,所以需要取模,并靠人品AC(1e9+7被卡掉了)。

#include <bits/stdc++.h>
using namespace std;

#define ll long long

const int maxn = 100010;
const int maxm = 100010;
const ll INF = 1e12;
const ll mod = 1e9+13;

int n,m,s,t;

int head[maxn];
int pre[maxm];
int _head[maxn];
int _pre[maxm];
int cnt;

void init(){
	memset(head,-1,sizeof(head));
	memset(_head,-1,sizeof(_head));
	cnt=1;
}

void addedge(int u,int v){
	pre[cnt]=head[u];
	head[u]=cnt;
	_pre[cnt]=_head[v];
	_head[v]=cnt;
	cnt++;
}

int a[maxm];
int b[maxm];
int l[maxm];

ll dist_s[maxn];
ll dist_t[maxn];
ll way_s[maxn];
ll way_t[maxn];

bool vis[maxn];

struct node{
	int u;
	ll dist;
	node(int u,ll dist):u(u),dist(dist){}
	node(){}
	bool operator<(const node &other)const{
		return dist>other.dist;
	}
};

void dijkstra(int s,ll *d,ll *w,int *head,int *pre){
	for(int i=1;i<=n;i++){
        d[i]=INF;
	}
	w[s]=1;
	d[s]=0;
	priority_queue<node> pq;
	pq.push(node(s,0));
	while(pq.size()){
		node cur = pq.top();	pq.pop();
		if(vis[cur.u])continue;

		for(int i=head[cur.u];i!=-1;i=pre[i]){
            int v=b[i];
            ll tmp=d[v];
            if(d[cur.u]+l[i]<d[v]){
				w[v]=w[cur.u];
				tmp=d[cur.u]+l[i];
				d[v]=tmp;
				pq.push(node(v,tmp));
            }else if(d[cur.u]+l[i]==d[v]){
				w[v]+=w[cur.u];
				d[v]=tmp;
            }
            w[v]%=mod;
		}
		vis[cur.u]=1;
	}

}

int main(){
	init();
	cin>>n>>m>>s>>t;
	for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a[i],&b[i],&l[i]);
        addedge(a[i],b[i]);
	}

	dijkstra(s,dist_s,way_s,head,pre);
	for(int i=1;i<=m;i++)swap(a[i],b[i]);
	memset(vis,0,sizeof(vis));
	dijkstra(t,dist_t,way_t,_head,_pre);
	for(int i=1;i<=m;i++)swap(a[i],b[i]);

	for(int i=1;i<=m;i++){
		ll need = dist_s[a[i]]+dist_t[b[i]]+l[i] - dist_s[t];
        if(0==need && (way_s[a[i]]*way_t[b[i]])%mod==way_s[t] ){
			printf("YES\n");
        }else{
			if(l[i]-need-1>0){
				printf("CAN %I64d\n",need+1);
			}else{
				printf("NO\n");
			}
        }
	}
    return 0;
}

        不管怎么说,取mod的方法只能人品AC,正解应该是找桥,在最短路上的桥是YES,其他是CAN。要注意重边的问题。。

        另外,我有一个疑惑,问什么资料上的tarjan喜欢用dfn更新low。。如果都用low更新,不是更简洁,统一吗。求大神解答。。

#include <bits/stdc++.h>
using namespace std;

#define ll long long

const int maxn = 100010;
const int maxm = 100010;
const ll INF = 1e12;

int n,m,s,t;

int a[maxm];
int b[maxm];
int l[maxm];

ll dist_s[maxn];
ll dist_t[maxn];

bool vis[maxn];

int head[maxn];
int pre[maxm];
int _head[maxn];
int _pre[maxm];
int cnt;
int dfn[maxn];
int low[maxn];
int dfstime;
int sta[maxn];
int statop;

struct Edge{
	int v;
	int id;
}edge[maxm<<1];

int sphead[maxn];
int sppre[maxm<<1];
bool isUnique[maxm];

void init(){
    memset(head,-1,sizeof(head));
    memset(_head,-1,sizeof(_head));
    memset(sphead,-1,sizeof(sphead));
    cnt=1;
    dfstime=0;
    statop=0;
}

void addedge(int u,int v){
    pre[cnt]=head[u];
    head[u]=cnt;
    _pre[cnt]=_head[v];
    _head[v]=cnt;
    cnt++;
}

struct node{
    int u;
    ll dist;
    node(int u,ll dist):u(u),dist(dist){}
    node(){}
    bool operator<(const node &other)const{
        return dist>other.dist;
    }
};

void dijkstra(int s,ll *d,int *head,int *pre){
    for(int i=1;i<=n;i++){
        d[i]=INF;
    }
    d[s]=0;
    priority_queue<node> pq;
    pq.push(node(s,0));
    while(pq.size()){
        node cur = pq.top();    pq.pop();
        if(vis[cur.u])continue;

        for(int i=head[cur.u];i!=-1;i=pre[i]){
            int v=b[i];
            ll tmp=d[v];
            if(d[cur.u]+l[i]<d[v]){
                tmp=d[cur.u]+l[i];
                d[v]=tmp;
                pq.push(node(v,tmp));
            }else if(d[cur.u]+l[i]==d[v]){
                d[v]=tmp;
            }
        }
        vis[cur.u]=1;
    }
}

void addsp(int u,int v,int id){
	edge[cnt].v=v;	edge[cnt].id=id;
	sppre[cnt]=sphead[u];
	sphead[u]=cnt++;

	edge[cnt].v=u;	edge[cnt].id=id;
	sppre[cnt]=sphead[v];
	sphead[v]=cnt++;
}

void tarjan(int u,int fa){
	dfn[u]=dfstime;
	low[u]=dfstime++;
	sta[statop++]=u;
	vis[u]=1;
	for(int i=sphead[u];i!=-1;i=sppre[i]){
		int v=edge[i].v;
		if((i^1)==fa)continue;
		if(!vis[v]){
			tarjan(v,i);
		}
		low[u]=min(low[u],low[v]);


		if(low[v]>dfn[u]){
            isUnique[edge[i].id]=1;
			while(statop>0){
				int cur=sta[--statop];
				if(cur==u)break;
			}
		}
	}
}

int main(){
    init();
    cin>>n>>m>>s>>t;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&a[i],&b[i],&l[i]);
        addedge(a[i],b[i]);
    }

    dijkstra(s,dist_s,head,pre);
    for(int i=1;i<=m;i++)swap(a[i],b[i]);
    memset(vis,0,sizeof(vis));
    dijkstra(t,dist_t,_head,_pre);
    for(int i=1;i<=m;i++)swap(a[i],b[i]);


	cnt=0;
	memset(vis,0,sizeof(vis));
    for(int i=1;i<=m;i++){
        ll need = dist_s[a[i]]+dist_t[b[i]]+l[i] - dist_s[t];
        if(0==need){
			addsp(a[i],b[i],i);
        }
    }
    tarjan(s,-1);

    for(int i=1;i<=m;i++){
        ll need = dist_s[a[i]]+dist_t[b[i]]+l[i] - dist_s[t];

		if(isUnique[i]){
			printf("YES\n");
		}else{
			if(l[i]-need-1>0){
				printf("CAN %I64d\n",need+1);
			}else{
				printf("NO\n");
			}
		}
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值