【题目】
UOJ
一幅
n
n
n个点
m
m
m条有向带边权
w
i
w_i
wi的图,每条边还有一个红绿白三色之一。
称一条从
1
1
1开始,最后停在某个城市
v
v
v的路径为发财路径当且仅当路径上存在负环。
现在可以选定一个数
−
1
0
30
≤
x
≤
1
0
30
-10^{30}\leq x \leq 10^{30}
−1030≤x≤1030,将每条红色边加上这个权,每条绿色边减去这个权。
问对于所有的
v
v
v,有多少个
x
x
x可以使得不存在发财路径,若数量大于
1
0
18
10^{18}
1018输出
−
1
-1
−1
n ≤ 100 , m ≤ 10000 , ∣ w i ∣ ≤ 1 0 9 n\leq 100,m\leq 10000,|w_i|\leq 10^9 n≤100,m≤10000,∣wi∣≤109
【解题思路】
雅礼的时候趣题选讲讲过,然而并没有怎么听懂,对于
Bellman-Ford
\text{Bellman-Ford}
Bellman-Ford这个算法并不是很熟悉。
这个算法的大概思路是设 f i , j f_{i,j} fi,j表示走了 i i i步到达 j j j的最短路长度,若 f n , j < f n − 1 , j f_{n,j}<f_{n-1,j} fn,j<fn−1,j,则节点 j j j在负环上。
对于此题由于有一个加权,不妨多加一维,设
f
i
,
j
,
k
f_{i,j,k}
fi,j,k表示走了
i
i
i步,到达
j
j
j,
x
x
x的系数为
k
k
k,所需的最短距离。那么我们可以得到出现负环的条件:
k
x
+
f
n
,
i
,
k
<
j
x
+
f
n
−
1
,
i
,
j
kx+f_{n,i,k}<jx+f_{n-1,i,j}
kx+fn,i,k<jx+fn−1,i,j
由于一个负环影响整个连通块,那么对于每个连通块中的点,可行的
x
x
x需要满足:
min
{
k
x
+
f
n
,
i
,
k
}
≥
min
{
j
x
+
f
n
,
i
,
j
}
\min \{ kx+f_{n,i,k}\}\geq \min \{ jx+f_{n,i,j}\}
min{kx+fn,i,k}≥min{jx+fn,i,j}
要解这个不等式,首先对于每个
k
k
k,枚举所有
j
j
j,求出下面解集的补集:
k
x
+
f
n
,
i
,
k
≥
min
{
j
x
+
f
n
,
i
,
j
}
kx+f_{n,i,k} \geq \min \{ jx+f_{n,i,j}\}
kx+fn,i,k≥min{jx+fn,i,j}
然后对于所有补集求并后再求补集即可。
最后解集的形式一定为 ( − ∞ , t ] , [ l , r ] , [ t , + ∞ ) (-\infty,t],[l,r],[t,+\infty) (−∞,t],[l,r],[t,+∞)中的一种(因为对于一个环有负环时满足条件的 x x x取值一定可以取到一边的极限)
复杂度大概是
O
(
n
3
)
O(n^3)
O(n3)
【参考代码】(是抄的)
#include<bits/stdc++.h>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const int N=105,M=10005;
const ll inf=1e18;
int n,m;
bool mp[N][N];
ll f[N][N][N<<1];
vector<pii>vec[N],st;
struct edge{int u,v,w,s;}e[M];
ll Ceil(ll a,ll b);
ll Floor(ll a,ll b)
{
if(a<0)a=-a,b=-b;
if(b<0)return -Ceil(a,-b);
return a/b;
}
ll Ceil(ll a,ll b)
{
if(a<0)a=-a,b=-b;
if(b<0)return -Floor(a,-b);
return (a-1)/b+1;
}
void gmin(ll &x,ll y){x=min(x,y);}
void gmax(ll &x,ll y){x=max(x,y);}
void init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) mp[i][i]=1;
for(int i=1;i<=m;++i)
{
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].w,&e[i].s);
mp[e[i].u][e[i].v]=1;
}
for(int k=1;k<=n;++k) for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
mp[i][j]|=mp[i][k]&mp[k][j];
}
void bellmanford()
{
memset(f,0x3f,sizeof(f));f[0][1][n]=0;
for(int i=1;i<=n;++i) for(int k=0;k<=2*n;++k)
{
for(int j=1;j<=n;++j) f[i][j][k]=f[i-1][j][k];
for(int j=1;j<=m;++j) if(k-e[j].s>=0 && k-e[j].s<=2*n)
gmin(f[i][e[j].v][k],f[i-1][e[j].u][k-e[j].s]+e[j].w);
}
}
void solve()
{
for(int i=1;i<=n;++i) for(int j=0;j<=2*n;++j) if(f[n][i][j]<inf)
{
ll l=-inf,r=inf;
for(int k=0;k<=2*n;++k) if(f[n-1][i][k]<inf)
{
if(j==k){if(f[n-1][i][j]==f[n][i][j])r=-inf,l=inf;}
else if(j<k) gmax(l,Floor(f[n-1][i][k]-f[n][i][j],j-k)+1);
else gmin(r,Ceil(f[n-1][i][k]-f[n][i][j],j-k)-1);
}
if(l<=r) vec[i].pb(mkp(l,r));
}
for(int i=1;i<=n;++i)
{
st.clear();
for(int j=1;j<=n;++j) if(mp[j][i])
{for(int k=0;k<(int)vec[j].size();++k) st.pb(vec[j][k]);}
sort(st.begin(),st.end());
ll l=-inf,r=-inf-1,sum=2*inf+1;
for(int j=0;j<(int)st.size();++j)
if(st[j].fi<=r) gmax(r,st[j].se);
else sum-=r-l+1,l=st[j].fi,r=st[j].se;
sum-=r-l+1;
if(sum>=inf/100) puts("-1"); else printf("%lld\n",sum);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("UOJ32.in","r",stdin);
freopen("UOJ32.out","w",stdout);
#endif
init();bellmanford();solve();
return 0;
}