CodeForces - 715B Complete The Graph
\(\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;
}