\(\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;
}