【51nod】1340 地铁环线
今天头非常疼,躲在家里没去机房
反正都要颓废了,然后花了一上午研究了一下这道神题怎么做……
题解
首先我们发现,如果我们设\(dis[i]\)为从\(0\)节点走到\(i\)节点的距离
那么题目中给出的所有关系都变成了\(n\)个变量中两两的大小关系式
这像什么,差分约束哇(听说这是noip知识点?可我noip的时候根本没听过哇qwq)
可是在列关系式的时候,你发现对于\(a -> b\)当(b < a)的时候,我们还需要一个值,就是环的总长,正好是我们需要统计的东西……
我们先看看关系式怎么列,设环的总长为\(x\)
对于边长至少大于1的话,可以把它当成第一种限制
我们对于\((a,b) >= d\)当\(a < b\)的时候
\(dis[b] - dis[a] >= d\)我们换成\(<=\)号就是\(dis[a] - dis[b] <= -d\)连一条\((b,a) = -d\)
当\(a > b\)
\(dis[b] + x - dis[a] >= d\)我们换成\(<=\)号就是\(dis[a] - dis[b] <= x - d\)连一条\((b,a) = x-d\)
对于\((a,b) <= d\)当\(a < b\)的时候
\(dis[b] - dis[a] <= d\)连一条\((a,b) = d\)
对于\((a,b) <= d\)当\(a > b\)的时候
\(dis[b] + x - dis[a] <= d\)连一条\((a,b) = -x + d\)
这里,如果我们把x换成特定的值,我们只要检查一下整张图有没有负环,用Bellman-ford,就可以判断这个x合不合法
当然啦,这个算法肯定跑不过去的,我们再来看一下我们建出的图
如果我们把x当成变量,我们会发现我们每一条边都是一个一次函数,我们设\(dis[s][k]\)是从\(0\)点走到\(s\)点,一次函数的一次项系数是\(k\)
\(dis[s][k]\)是里存的是一次函数\(kx + b\)里最小的那个\(b\)(因为最短路我们总要尽量少啊)
那么,我们可以用这些一次函数拼起来,我们这个点在整个x值域上的最小值!只要维护一个栈,斜率从大到小往里面加直线,栈里面的直线同时记录一下这条直线控制着哪段区间上的最小值就可以
再把每条边取出来,我们发现我们的关系式是
\(dis(u,k) + e(u,v) >= dis(v,k)\)
也就是,我们需要知道\(dis(u,k) + e(u,v)\)在整个值域上的函数(也就是无数个分段一次函数拼出的最小值,它长成了一个上凸包),和\(dis(v,k)\)在整个值域上的函数,我们要取这两个函数中,\(dis(u,k) + e(u,v)\)在\(dis(v,k)\)上面的部分,从前往后拿出直线来比较即可
最后我们对于每个边得到了很多区间,我们要求出所有边的区间交,可以把这些区间设置成左端点+1(右端点+1的位置)-1的差分,当一个区间的值是边的数量时,就把这段区间统计进答案
而对于解有无穷多的情况,我们设置值域是n到正无穷,如果我们的答案和正无穷很接近了,那么就是-1了
写到这,我不禁想到了一个往边权里填多项式的另一道题,叫BIKE,一看这个题目大概就知道是谁出的了。。
看来这还是一个比较神奇的技巧啊><
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
//#define ivorysi
#define fi first
#define se second
#define mp make_pair
#define inf (1LL << 55)
#define space putchar(' ')
#define enter putchar('\n')
using namespace std;
typedef long long int64;
int N,M1,M2,tot,p1,p2;
int64 dis[55][105];
pair<int64,int> P[100005];
template<class T>
void read(T &res) {
res = 0;T f = 1;char c = getchar();
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {x = -x;putchar('-');}
if(x >= 10) {
out(x / 10);
}
putchar('0' + x % 10);
}
struct Line {
int64 L,R,k,b;
int64 f(int64 x) {
return k * x + b;
}
}L1[10005],L2[10005];
int id;
void insert(int64 L,int64 R) {
P[++tot] = mp(L,1);
P[++tot] = mp(R + 1,-1);
}
int64 up(int64 A,int64 B) {
if(B < 0) B = -B,A = -A;
int64 C = A / B;
return C + (C * B < A);
}
int64 down(int64 A,int64 B) {
if(B < 0) B = -B,A = -A;
int64 C = A / B;
return C - (C * B > A);
}
void push(Line *a,int &len,Line t) {
while(len && t.f(a[len].L) <= a[len].f(a[len].L)) --len;
if(len) {
int64 B = -(a[len].b - t.b),K = (a[len].k - t.k);
t.L = down(B,K) + 1;
a[len].R = t.L - 1;
}
a[++len] = t;
}
void check(Line U,Line D) {
int64 L = max(U.L,D.L),R = min(U.R,D.R);
if(L > R) return;
Line t;t.k = U.k - D.k,t.b = U.b - D.b;
if(t.f(L) >= 0) {
if(t.f(R) >= 0) insert(L,R);
else insert(L,down(-t.b,t.k));
}
else if(t.f(R) >= 0) {insert(up(-t.b,t.k),R);}
}
struct node{
int s,t,k;int64 b;
void update() {
for(int i = 0 ; i <= 2 * N ; ++i) {
if(i + k >= 0 && i + k <= 2 * N) {
dis[t][i + k] = min(dis[s][i] + b,dis[t][i + k]);
}
}
}
void calc() {
p1 = 0,p2 = 0;
for(int i = N * 2 ; i >= 0 ; --i) {
if(dis[s][i] < inf / 2)
push(L1,p1,(Line){(int64)N,inf,(int64)(i - N + k),dis[s][i] + b});
if(dis[t][i] < inf / 2)
push(L2,p2,(Line){(int64)N,inf,(int64)(i - N),dis[t][i]});
}
int t1 = 1,t2 = 1;
for( ;t1 <= p1 && t2 <= p2 ;) {
check(L1[t1],L2[t2]);
if(L1[t1].R <= L2[t2].R) ++t1;
else ++t2;
}
}
}E[10005];
int sumE;
void Solve() {
read(N);read(M1);read(M2);
for(int i = 0 ; i <= N ; ++i) {
for(int j = 0 ; j <= 2 * N ; ++j) dis[i][j] = inf;
}
dis[0][N] = 0;
sumE = 0;
int s,t;int64 d;
for(int i = 0 ; i < N ; ++i) {
int s = i,t = (i + 1) % N;
E[++sumE] = (node){t,s,(t < s),-1};
}
for(int i = 1 ; i <= M1 ; ++i) {
read(s);read(t);read(d);
E[++sumE] = (node){t,s,(t < s),-d};
}
for(int i = 1 ; i <= M2 ; ++i) {
read(s);read(t);read(d);
E[++sumE] = (node){s,t,-(t < s),d};
}
for(int i = 1 ; i <= N ; ++i) {
for(int j = 1 ; j <= sumE ; ++j) {
E[j].update();
}
}
tot = 0;
for(int j = 1 ; j <= sumE ; ++j) id = j,E[j].calc();
P[++tot] = mp(inf + 1,0);
sort(P + 1,P + tot + 1);
s = 0;
int64 ans = 0;
for(int i = 1 ; i <= tot ; ++i) {
s += P[i].se;
if(s == sumE) {
ans += P[i + 1].fi - P[i].fi;
}
}
if(ans > (inf / 4)) ans = -1;
out(ans);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
int T;
read(T);
while(T--) {
Solve();
}
}