不难看出这是一道差分约束的题目。

但是如果想按照通常的题目那样去建边的话,就会发现这句话——相邻两站的距离至少是1公里——建边后就直接让整个题出现了负环(默认是按求最短路建边),没法做了。

这时我们就需要使用断环为链的技巧。

可以设\(len\)为地铁环线总长

那么就需要把\(a→b(a>b)\)的限制条件转换为\(b→a\)的限制条件,比如\(dis(a,b)\leq k\)转换为\(dis(b,a)\geq len-k\)。总算能连边建图惹

如果现在要判断是否有解,那方法肯定是\(spfa\)判负环。

但问题在于\(len\)是未知量,如果用\(len\)表示每一条边,那么\(e_i=len\pm d_i\ or\ \pm\ d_i\)。

因为有没有解的关键在于有没有负环,所以:

考虑图上每一个环, 其权值一定可以表示为 \(val=k×len+b\) 的形式. 那么现在分类讨论一下每一个环.

  • \(k=0\) 且 \(b<0\) 时, \(val<0\),为负环,一定无解;
  • \(k<0\) 且 \(b<0\) 时, \(val<0\),为负环,一定无解;
  • \(k<0\) 且 \(b>0\) 时, 如果出现\(val<0\),可以通过减小 \(len\) 来消除负环;
  • \(k>0\) 时, 如果出现\(val<0\),可以通过增大 \(len\) 来消除负环。

这样思考会发现\(len\)的合法大小一定是一段连续区间

所以可以确定一个\(INF\),然后二分\(len\),由上述规律找到\(len\)的合法区间的左右端点,如果右端点接近\(INF\)则判断为无数个解。

\(tips\):

  • 凡是没有说明联通的图都要小心;
  • 可能只有一个点;
  • 因为\(e_i=len\pm d_i\ or\ \pm\ d_i\),而\(len\)是变量,所以建边的时候存\(len\)的系数和\(d_i\);
  • 若一个点入队次数超过n,则有负权环,然后要判断\(k\)的值,来决定二分方向,要记录来时的边 废话,并且把环取出来 可能有小尾巴呀;

代码:

#include <set>
#include <map>
#include <queue>
#include <vector>
#include <math.h>
#include <string>
#include <utility>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <iostream>
#include <algorithm>
using namespace std; struct edge{
int nx,from,to,k,v;
}; #define N 55
#define M 55
#define INF 100000000000
#define mid ((l+r)>>1) int T,n,m1,m2;
int cnt,head[N];
int st,pre[N],ar[N],inq[N],vis[N];
long long L,R,dis[N];
queue <int> Q;
edge e[M*2+N*2]; inline void clear(){
cnt=L=R=0;
memset(head,0,sizeof(head));
} inline void add(int u,int v,int k,int w){
e[++cnt]=edge{head[u],u,v,k,w};
head[u]=cnt;
} inline int spfa(long long len){
while(!Q.empty()) Q.pop();
memset(ar,0,sizeof(ar));
memset(inq,0,sizeof(inq));
memset(pre,0,sizeof(pre));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i) dis[i]=INF;
Q.push(st); inq[st]=1; dis[st]=0;
while(!Q.empty()){
int x=Q.front(); Q.pop(); inq[x]=0;
for(int i=head[x];i;i=e[i].nx){
int y=e[i].to;
if(dis[y]>dis[x]+e[i].k*len+e[i].v){
dis[y]=dis[x]+e[i].k*len+e[i].v;
pre[y]=i; ar[y]=ar[x]+1;
if(ar[y]>n+1){
int tail,k=0;
for(int j=y;j;j=e[pre[j]].from)
if(vis[j]){tail=j;break;}
else vis[j]=1;
k+=e[pre[tail]].k;
for(int j=e[pre[tail]].from;j!=tail;j=e[pre[j]].from)
k+=e[pre[j]].k;
if(k==0) return 1;
if(k>0) return 2;
if(k<0) return 3;
}
if(!inq[y]) inq[y]=1,Q.push(y);
}
}
}
return 4;
} int main(){
scanf("%d",&T);
while(T--){
clear();
scanf("%d%d%d",&n,&m1,&m2);
add(1,n,1,-1);
for(int i=2;i<=n;++i) add(i,i-1,0,-1);
for(int i=1;i<=n;++i) add(st,i,0,0);
int a,b,d;
for(int i=1;i<=m1;++i){
scanf("%d%d%d",&a,&b,&d); ++a,++b;
if(a<b) add(b,a,0,-d);
else add(b,a,1,-d);
}
for(int i=1;i<=m2;++i){
scanf("%d%d%d",&a,&b,&d); ++a,++b;
if(a<b) add(a,b,0,d);
else add(a,b,-1,d);
}
int flag;
bool OK=0;
long long l=1,r=INF;
while(l<=r){
flag=spfa(mid);
if(flag==1) break;
else if(flag==2) l=mid+1;
else if(flag==3) r=mid-1;
else OK=1,L=mid,r=mid-1;
}
l=1,r=INF;
while(l<=r){
flag=spfa(mid);
if(flag==1) break;
else if(flag==2) l=mid+1;
else if(flag==3) r=mid-1;
else OK=1,R=mid,l=mid+1;
}
if(!OK) printf("0\n");
else if(R>=INF) printf("-1\n");
else printf("%lld\n",R-L+1);
}
return 0;
}

参考了Rothen的博客

我也不知道他 blog 为啥没了,也不敢问

最新文章

  1. 【linux草鞋应用编程系列】_5_ Linux网络编程
  2. HTML中网页超链接设计
  3. 【iCore3 双核心板_FPGA】例程四:Tcl脚本实验——配置引脚
  4. U盘FAT32文件系统
  5. eclipse中使用git提交代码到github
  6. POJ 3076 Sudoku DLX精确覆盖
  7. 第十七章——配置SQLServer(3)——配置“对即时负载的优化”
  8. iOStextView的代理方法展示
  9. WebService应用--使用java开发WebService程序
  10. 理解Linux文档的默认安全机制、隐藏属性、特殊权限,妈妈在也不用担心你从删库到跑路!!!
  11. 并发编程futuretask
  12. Spring MVC @RequestMapping注解详解
  13. 使用nmon监控Linxu服务器性能
  14. 第12章 GPIO输入—按键检测
  15. vue router动态路由
  16. docker 运行容器时为容器起别名
  17. axios封装(二)队列管理
  18. WITH HINDSIGHT
  19. 老生常谈,HashMap的死循环(转)
  20. 【NIS】深入了解NIS

热门文章

  1. springcloud如何实现服务的注册?
  2. SpringBoot和SpringCloud?
  3. vue集成CKEditor构建框架的使用,遇到富文本框不出现工具栏等操作
  4. 什么是 spring 装配?
  5. 学习GlusterFS(七)
  6. 机器学习优化算法之EM算法
  7. 使用 Vuex + Vue.js 构建单页应用
  8. python-汇率兑换
  9. javaweb图书管理系统之不同用户跳转不同页面
  10. 微信小程序发送验证码功能,验证码倒计时