\(\text{Bi Bi Time!}\)

“好家伙。”

\(\text{Description}\)

传送门

\(\text{Solution}\)

首先可以把不定边都赋值为 \(1\) 跑一遍最短路,如果这样最短路大于 \(L\) 或不是通路就无解。

法一​

微调法。

考虑对任意一条不定边 \(+1\),每次最短路最多只会增加 \(1\),那么我们这样微调如果能取过 \(L\) 就一定能取到 \(L\)

如何微调?我们将不定边编一个序号,按这个顺序一次给每条不定边 \(+1\)(相当于一个优先级,但注意每次只能 \(+1\))。二分微调总次数,显然对于每条边就能求出这条边的增量,我们跑一遍最短路判定是否合法就行了。

不过难道不会出现序号后面需要 \(+1\),前面的那个不能 \(+1\) 的情况吗?

其实就是害怕改变最短路大小或最短路路径。

对于大小很好解决,让前面那个 \(+1\),最后一个改变最短路大小的不 \(+1\) 就可以了。

对于路径呢?如果改变了路径,就说明有更好的路径,这个路径和“前面的那个”的最短路路径等效,也不会有问题。

时间复杂度 \(\mathcal O(m\log n \log (m\times L))\)

这里贴一份知名博主的代码吧:

#include <cstdio>
#include <cstring>
#include <iostream> 
#include <queue>
using namespace std;
const int M = 100005;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,L,cnt,s,t,tot,f[M],a[M],b[M],c[M],tag[M],dis[M];
struct edge
{
	int v,c,next;
}e[2*M];
struct node
{
	int u,c;
	bool operator < (const node &b) const
	{
		return c>b.c;
	}
};
int dij()
{
	priority_queue<node> q;
	q.push(node{s,0});
	memset(dis,0x3f,sizeof dis);
	dis[s]=0;
	while(!q.empty())
	{
		node t=q.top();q.pop();
		if(dis[t.u]<t.c) continue;
		for(int i=f[t.u];i;i=e[i].next)
		{
			int v=e[i].v,c=e[i].c;
			if(dis[v]>dis[t.u]+c)
			{
				dis[v]=dis[t.u]+c;
				q.push(node{v,dis[v]});
			}
		}
	}
	return dis[t];
}
int check(int x)//返回序列长度是x的最短路长度 
{
	tot=0;
	memset(f,0,sizeof f);
	for(int i=1;i<=m;i++)
	{
		int ct=c[i];
		if(tag[i]) ct+=x/cnt+(x%cnt>=tag[i]);
		e[++tot]=edge{a[i],ct,f[b[i]]},f[b[i]]=tot;
		e[++tot]=edge{b[i],ct,f[a[i]]},f[a[i]]=tot;
	}
	return dij();
}
void print(int x)
{
	puts("YES");
	for(int i=1;i<=m;i++)
	{
		int ct=c[i];
		if(tag[i]) ct+=x/cnt+(x%cnt>=tag[i]);
		printf("%lld %lld %lld\n",a[i]-1,b[i]-1,ct);
	}
}
void dich(int l,int r)
{
	if(l>r) return;
	int mid=(l+r)>>1,t=check(mid);
	if(t==L)
	{
		print(mid);
		exit(0);
	}
	if(t>L) dich(l,mid-1);
	else dich(mid+1,r);
}
signed main()
{
	n=read();m=read();L=read();s=read()+1;t=read()+1;
	for(int i=1;i<=m;i++)
	{
		a[i]=read()+1;b[i]=read()+1;c[i]=read();
		if(c[i]==0) tag[i]=++cnt,c[i]=1;
	}
	dich(0,L*m);
	puts("NO");
}

法二

注意第一次的最短路(文首)从 \(t\) 开始,设这次算出的最短路数组为 \(dis\)

第二次从 \(s\) 开始做最短路,设这次算出的最短路数组为 \(d\)。遇到不定边 \((u,v)\) 就把这条边赋值为 \(w=\max\{1,L-dis_v-d_u\}\),显然如果赋值的边权大于 \(w\) 会使这条路径直接废掉,因为前面的已尘埃落定,后面的又已经是能做到最短的了。

那么如果这条边成功更新了 \(d_v\),我们就万事大吉。如果不行就说明已经算出的到 \(v\) 的路径有更短的,显然将边权赋值小于 \(w\) 也就不会更优了,还不如更短的那个。

时间复杂度 \(\mathcal O(m\log n)\)

\(\text{Code}\)

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
template <class T> inline T Swap(T &x,T &y) {x^=y^=x^=y;}

#include <queue>
using namespace std;
#define int long long
typedef long long ll;
typedef pair <ll,int> Pair;

const int maxn=1005,maxm=10005;
const ll inf=(1ll<<50);

int n,m,L,s,t,l[maxm],r[maxm],ans[maxm];
ll dis[maxn],d[maxn];
int head[maxn],nxt[maxm<<1],to[maxm<<1],val[maxm<<1],id[maxm<<1],cnt;
priority_queue <Pair> q;
bool vis[maxn];

void addEdge(int u,int v,int w,int ID) {
	nxt[++cnt]=head[u],to[cnt]=v,val[cnt]=w,head[u]=cnt,id[cnt]=ID;
	nxt[++cnt]=head[v],to[cnt]=u,val[cnt]=w,head[v]=cnt,id[cnt]=ID;
}

void Dijkstra1() {
	rep(i,1,n) dis[i]=inf;
	q.push(make_pair(0,t)); dis[t]=0;
	while(!q.empty()) {
		Pair t=q.top(); q.pop();
		int u=t.second;
		if(-t.first!=dis[u]||vis[u]) continue;
		vis[u]=1;
		erep(i,u) {
			if(vis[v]) continue;
			int w=val[i]?val[i]:1;
			if(dis[v]>dis[u]+w) {
				dis[v]=dis[u]+w;
				q.push(make_pair(-dis[v],v));
			}
		}
	}
}

void Dijkstra2() {
	rep(i,1,n) d[i]=inf,vis[i]=0;
	q.push(make_pair(0,s)); d[s]=0;
	while(!q.empty()) {
		Pair t=q.top(); q.pop();
		int u=t.second;
		if(d[u]!=-t.first||vis[u]) continue;
		vis[u]=1;
		erep(i,u) {
			if(vis[v]) continue;
			int w=val[i];
			if(!w) ans[id[i]]=w=Max(1ll,0ll+L-d[u]-dis[v]);
			if(d[v]>d[u]+w) {
				d[v]=d[u]+w;
				q.push(make_pair(-d[v],v));
			}
		}
	}
}

signed main() {
	int x,y,z;
	n=read(9),m=read(9),L=read(9),s=read(9)+1,t=read(9)+1;
	rep(i,1,m) {
		x=read(9)+1,y=read(9)+1,z=read(9);
		l[i]=x-1,r[i]=y-1,ans[i]=z;
		addEdge(x,y,z,i);
	}
	Dijkstra1(); Dijkstra2();
	if(d[t]^L) return puts("NO"),0;
	puts("YES");
	rep(i,1,m) printf("%lld %lld %lld\n",l[i],r[i],ans[i]);
	return 0;
}
posted on 2020-12-23 17:09  Oxide  阅读(242)  评论(0编辑  收藏  举报