CF715B Complete The Graph
一、题目
二、解法
这道题可以考虑微调法,也就是首先把不定权值都设置成 \(1\),然后再慢慢调整
具体来说,我们把某一个不定权值增加 \(1\) ,你会发现最短路不会降低,而且最多增加 \(1\) 的。这样如果我们微调了足够的次数就一定能取到要的最短路。
现在我们考虑加速微调的过程,设 \(x_i\) 为第 \(i\) 个不定边,那么我们依次调整 \(x_1,x_2.....\) 可以二分微调的次数,那么所有边的权值都知道了。计算出最短路之后,我们用最短路长度去判断下一次往哪边分。不难发现如果存在答案的话是一定能被二分出来的。
时间复杂度 \(O(m\log n\log (ml))\),虽然复杂度并不优秀但是思路很值得借鉴。
还有一个跑两遍最短路的做法,也就是第二次让 \(s\) 到所有点的最短路都增加一开始最短路和目标长度的差值,这样的复杂度 \(O(m\log n)\),虽然十分优秀但我感觉十分难想。最后给出微调法的代码:
#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");
}