Codeforces Round #372 (Div. 1) B. Complete The Graph

时间:2023-02-02 19:23:53

题目链接:传送门

题目大意:给你一副无向图,边有权值,初始权值>=0,若权值==0,则需要把它变为一个正整数(不超过1e18),现在问你有没有一种方法,

     使图中的边权值都变为正整数的时候,从 S 到 T 的最短路恰好等于 L。

     若没有输出 "NO",否则输出 "YES",同时输出新图中的所有边权值。

题目思路:二分+最短路(spfa or dijkstra)

     

闲谈:先%一发杜教,思路来源于看他的代码。然后蒟蒻spfa 982ms,杜教 dijkstra 93ms(毕竟1000个点,严格nlogn,spfa不稳定)

 

详细方法:

     以下把题目中权值为0 的边称为 重构边

             无穷大设为1e9+1(因为 L 最大1e9,所以只要重构边权值为1e9+1,则任何经过该重构边的路径均不合法,因为>L)

     题目要求所有边都要为正,那么我们验证时首先验证 重构边 权值为 1 的情况。

        若此时从 S 到 T 的最短路 >L,则为NO

        因为我们在题目要求范围里无法找到一条路径使 S 到 T 的最短路更短,所以不成立

     再验证所有重构边 权值为 无穷大的情况。

        若此时从 S 到 T 的最短路 <L,同样为NO

        因为重构边设为无穷大之后,最短路只会走原图上的 非重构边,而这些边权值是不能改变的,及时重构边边权减小,

        也只会使 最短路变得更短,所以不合法

     这就是不合法的情况,除此之外的所有情况都会找到一种重构方法使题目有解。

     上面第一种方法如果最短路<L,则可通过增加重构边边权的方法使最短路==L,第二种情况同理可减小边权使之成立

 

分割线/***----------------------------------------------------------------------------------------------------------------------***/

 

    现在是如何找到一种边权分配方法使最短路==L

    首先我们要弄明白一个结论

        1.如果最短路不经过重构边(对应上面第二种情况)且最短路恰等于L,则重构边边权任意正整数。

        2.如果最短路经过重构边{

          无论最短路==L的路径有多少条,我们只需找到一条路径即可,其他路径上的重构边权值就算设为无穷大

          对题目没有影响(因为已经找到一条)。我们只需要把选好的这条路径上的重构边边权设置好就行。

        }

        那么怎样设置边权?

        可能你会问为什么不先找一条路径?其实我们只需要不断给每条边分配边权并且匹配最短路是否合法就行。

        也就是说我们给每条重构边机会,如果不行,把它设为无穷大,否则分配边权给它看它是否能满足我们最短路的要求。

        这样会很简单也不会出错。

 

        设置边权的过程用二分来实现,二分的值是所有边的权值总和。然后从给每一条边分配权值,如果最短路==L,则成功

        否则如果最短路<L,增加边权。如果最短路>L,减少边权。

        最短路用 spfa or dijkstra 即可。此题点少,dijkstra很快。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <cstring>
 7 #include <stack>
 8 #include <cctype>
 9 #include <queue>
10 #include <string>
11 #include <vector>
12 #include <set>
13 #include <map>
14 #include <climits>
15 #define lson rt<<1,l,mid
16 #define rson rt<<1|1,mid+1,r
17 #define fi first
18 #define se second
19 #define ping(x,y) ((x-y)*(x-y))
20 #define mst(x,y) memset(x,y,sizeof(x))
21 #define mcp(x,y) memcpy(x,y,sizeof(y))
22 using namespace std;
23 #define gamma 0.5772156649015328606065120
24 #define mod 1000000007
25 #define inf 0x3f3f3f3f
26 #define N 1005050
27 #define maxn 20005
28 typedef pair<int,int> PII;
29 typedef long long LL;
30 
31 int n,m,k,x,y,v;
32 int S,T,L;
33 int head[N],hcnt;
34 struct Node{
35     int to,nxt,v;
36 }node[maxn];
37 int d[N],vis[N],s[maxn];
38 vector<int>eg;  ///存重构边
39 queue<int>q;
40 int spfa(){   
41     for(int i=0;i<n;++i) d[i]=(int)1e9+1; d[S]=0;
42     mst(vis,0); q.push(S);
43     while(!q.empty()){
44         int x=q.front();q.pop(); vis[x]=0;
45         for(int i=head[x];~i;i=node[i].nxt){
46             int e=node[i].to;
47             if(d[e]>d[x]+node[i].v){
48                 d[e]=d[x]+node[i].v;
49                 if(!vis[e]){vis[e]=1;q.push(e);}
50             }
51         }
52     }
53     return d[T];
54 }
55 int check(LL sum){  ///判断是否合法
56     for(int u:eg){
57         node[u].v=node[u|1].v=1+min(sum,1000000000ll); 
58                     ///给每个重构边分配权值
59         sum-=node[u].v-1;
60     }
61     return spfa();
62 }
63 inline void add(int x,int y,int v){
64     node[hcnt].to=y;node[hcnt].nxt=head[x];node[hcnt].v=v;head[x]=hcnt++;
65 }
66 int main(){
67     int i,j,group;
68     mst(head,-1);
69     scanf("%d%d%d%d%d",&n,&m,&L,&S,&T);
70     for(i=1;i<=m;++i){
71         scanf("%d%d%d",&x,&y,&v);
72         if(!v)eg.push_back(hcnt);
73         s[hcnt]=x;
74         add(x,y,v);
75         add(y,x,v);
76     }
77     LL l=0,r=eg.size()*LL(1e9);
78     if(check(0)>L||check(r)<L)return 0*printf("NO\n");
79     LL ans;
80     while(l<=r){
81         LL mid=l+r>>1,temp=check(mid);
82         if(temp>L)ans=mid,r=mid-1;
83         else if(temp<L)l=mid+1;
84         else break;
85     }
86     printf("YES\n");
87     for(i=0;i<m*2;i+=2)printf("%d %d %d\n",s[i],node[i].to,node[i].v);
88     return 0;
89 }