题目描述
R 君和小伙伴打算一起玩赛车。但他们被老司机 mocania 骗去了秋名山。
秋名山上有 n 个点和 m 条边,R 君和他的小伙伴要从点 1 出发开往点 n,每条边都有一个初始的方向。老司机 mocania 拿到了秋名山的地图但却不知道每条路有多长。显然,为了赛车游戏的公平,每条 11 到 nn 的路径应当是等长的。mocania 想,我就随便给边表上一个 1...9 的长度,反正傻傻的 R 君也看不出来。
可 mocania 的数学不大好,不知道怎么给边标长度,只能跑来请教你这个 OI 高手了。
输入格式
第一行两个整数 n,m。
接下来 mm 行,每行两个整数 u,v,表示一条从 u 到 v 的有向边。
输出格式
如果无解或者不存在 1 到 n 的路径直接输出一个 -1。
如果有解第一行输出两个数 n,m,和输入文件中给出的相同。
借下来 m 行,每行三个整数 u,v,w,表示把从 u 到 v 的路径的长度设置为 w,其中 ww 是一个 1...9 的整数。要求所有边的出现顺序和题目中给出的相同。
输入输出样例
输入 #1复制
10 10
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
1 10
输出 #1复制
10 10
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
6 7 1
7 8 1
8 9 1
9 10 1
1 10 9
说明/提示
数据范围
本题启用 Special Judge 和 Subtask。
Subtask #1:30% 的分数,n <= 10, m <= 20
Subtask #2:60% 的分数,n <= 100, m <= 200
Subtask #3:100% 的分数,n <= 1000, m <= 2000
保证数据中不会出现重边,自环。
这个是luogu10月月赛的题,当时被虐到只会puts("0")
现在。。。只会puts("-1")...
差分约束
题目要求每个边的长度都在1到9之间。也就是说对于一个边(u ,v) 有 1 <= dis[v] - dis[u] <= 9
我个人喜欢大连小,所以 连边(v , u , 1) , (u , v , -9)
注意 开始时dis不能赋成0,有负数。。。
还有要把不在1到n路径上的边忽略,不给他们连差分约束的边。。
这个可以用一正一反两边dfs搞定。。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
const int N = 1010;
inline int read()
{
register int x = 0 , f = 0; register char c = getchar();
while(c < '0' || c > '9') f |= c == '-' , c = getchar();
while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
return f ? -x : x;
}
int n , m , cnt;
int head[N] , eu[N<<1] , ev[N<<1] , vis[N] , t[N] , dis[N] , bo[N] , bo1[N] , bo2[N];
struct edge{ int v , nex , c; }e[N<<2];
inline void add(int u , int v , int c) { e[++cnt].v = v; e[cnt].nex = head[u]; e[cnt].c = c; head[u] = cnt; return ; }
void dfs1(int x)
{
bo1[x] = 1;
for(int i = head[x]; i ; i = e[i].nex) if(i & 1) // 求出1能到那些点
if(!bo1[e[i].v]) dfs1(e[i].v);
return ;
}
void dfs2(int x)
{
bo2[x] = 1;
for(int i = head[x] ; i ; i = e[i].nex) if(!(i & 1)) // 求出那些点能到n
if(!bo2[e[i].v]) dfs2(e[i].v);
return ;
}
queue<int> q;
bool SPFA()
{
for(int i = 2 ; i <= n ; ++i) dis[i] = -1e8; q.push(1);
while(q.size())
{
int x = q.front(); q.pop(); vis[x] = 0; if((++t[x]) >= n) return false; // 此处是 >= n , .. 不是n-1 , 反正写多点没错。
for(int i = head[x] , v; i ; i = e[i].nex)
{
v = e[i].v;
if(dis[v] < dis[x] + e[i].c)
{
dis[v] = dis[x] + e[i].c;
if(!vis[v]) vis[v] = 1 , q.push(v);
}
}
}
return true;
}
int main()
{
n = read(); m = read();
for(int i = 1 ; i <= m ; ++i) eu[i] = read() , ev[i] = read() , add(eu[i] , ev[i] , 0) , add(ev[i] , eu[i] , 0);
dfs1(1);
if(!bo1[n]) { puts("-1"); return 0; } // 1 不能到 n 不连通
dfs2(n);
for(int i = 1 ; i <= n ; ++i) if(bo1[i] && bo2[i]) bo[i] = 1; bo[1] = bo[n] = 1; // bo = 1 是有用点
memset(head , 0 , sizeof head); cnt = 0; // 重复利用
for(int i = 1 ; i <= m ; ++i) // 建差分约束的边
if(bo[eu[i]] && bo[ev[i]]) add(eu[i] , ev[i] , -9) , add(ev[i] , eu[i] , 1);
if(!SPFA()) { puts("-1"); return 0; } // 无解
cout << n << ' ' << m << '
';
for(int i = 1 ; i <= m ; ++i) // 输出是判一下是不是在1到n的路径上
cout << eu[i] << ' ' << ev[i] << ' ' << ((bo[eu[i]] && bo[ev[i]]) ? dis[eu[i]] - dis[ev[i]] : 1) << '
';
return 0;
}