跳蚤公路
描述
跳蚤国由 n 个城市组成,编号为 1 到 n。
跳蚤国有 m 条连接城市的单向高速公路。经过高速公路当然是要收费的 —— 每条高速公路都有一个过路费 w (货币单位为跳蚤币),司机每次经过这条公路时都需要缴纳 w 跳蚤币。特别地,过路费可以是负的,这表示司机每次经过这条公路时政府都会给 −w 跳蚤币的补贴。
随着时代发展,跳蚤国王认为原有的高速公路规划已经不符合国情了。于是他根据交通拥堵情况把每条公路染成了红、绿、白三色之一,然后他会小心地选定一个数 x,再把每条红色公路的过路费涨 x 跳蚤币,把每条绿色公路的过路费降 x 跳蚤币,而白色公路的过路费不变。
虽然让绿色公路降过路费是好事,但是如果过路费降得太厉害,某个跳蚤司机就可以从 1 号城市出发在城市间转悠,最后停在某个城市 v,数一下自己的钱包发现自己竟然赚钱了。如果赚的钱超过 10100 10 100 跳蚤币,则我们称这样的路径为发财路径。
现在跳蚤国王还不太确定 x 的取值应该是多少,他当然讨厌这种丑陋的靠补贴费发财的行为。于是他希望助手伏特对于 v=1…n,求出使得不存在从 1 号城市出发到 v 号城市结束的发财路径的整数 x 的个数。
除此之外,xx必须介于 −1030 − 10 30 到 $10^{30} 之间。
伏特当然知道怎么做啦!但是他想考考你。
输入格式
第一行包括两个正整数 n 和 m。表示跳蚤国的城市数和高速公路数。
接下来 mm 行,每行四个用空格隔开的整数 v,u,w,s (1≤v,u≤n,s∈{−1,0,1}),表示一条从 v 号城市向 u 号城市的过路费为 w 的单向高速公路。如果 s=1 则为红色,s=0 则为白色,s=−1 则为绿色。
输出格式
输出 n 行,第 i 行包含一个整数表示 v=i 时满足条件的 x 的个数。如果 x 的个数超过 1018 10 18 个,请输出 −1。
C/C++ 输入输出 long long 时请用 %lld。C++ 可以直接使用 cin/cout 输入输出。
样例一
input
5 6
1 2 7 0
1 4 3 -1
2 5 4 0
2 3 1 -1
2 3 -3 1
3 2 1 0
output
-1
1
1
-1
1
explanation
对于任何一个 x 都找不到从 1 出发到 1,4 结束的发财路径。
只有当 x=2 时,才找不到从 1 出发到 2,3,5 结束的发财路径。
样例二
input
12 15
1 2 1 0
1 4 1000000000 0
1 6 2 0
2 3 2 0
4 4 -2 1
4 4 10 -1
4 3 1000000000 0
6 7 -5 0
7 8 10 -1
8 6 10 -1
7 9 6 -1
9 10 2 1
10 11 3 1
11 9 5 1
12 12 -233 0
output
-1
-1
9
9
-1
-1
-1
-1
11
11
11
-1
explanation
对于 3,4 号城市,无法发财的 x 的取值范围为 [2,10]。
对于 6,7,8 号城市,无法发财的 x 的取值范围为 [−1030,7.5]。
对于 9,10,11 号城市,无法发财的 x 的取值范围为 [−103,7.5]。由于要求是整数,所以只有介于 −3 到 7 的整数满足条件,共 11 个 x 的取值。
特别注意 1212 号城市,虽然不断地从 1212 号城市出发再回 1212 号城市就可以发财,但是 11 号城市无法到达 1212 号城市,所以所有 xx 都满足条件。
样例三
见样例数据下载。去掉所有的自环后图中不存在环。
样例四
见样例数据下载。
限制与约定
n≤100 m≤10000
时间限制:1s
空间限制:256MB
蒟蒻完全想不到怎么办QAQ
看完题解都不太会写怎么办QAQ
思路:
显然路过负权环的路径才是发财路径……
首先显然会有一个思路:
将环非负的条件表示成形如 kx+b≥0 k x + b ≥ 0 的形式,然后对于每个节点,将所有经过它的环的的式子的解取交集即可~
然而这本质上是爆枚负环,复杂度爆炸……
于是考虑一种叫Bellman-Ford判负环的东西。
Bellman-Ford的具体原理为,设 f[i][j] f [ i ] [ j ] 代表走 i i 步到达 j j 节点至少所需的最小距离,那么若出现负权环,则会有 f[n][i]<f[n−1][i] f [ n ] [ i ] < f [ n − 1 ] [ i ] ,即经过了 n−1 n − 1 条以上的边比经过 n−1 n − 1 条边距离更短。
对于此题,可以使用类似的方法找负环,只是需要另加一维,令 f[i][j][k] f [ i ] [ j ] [ k ] 代表走 i i 步到达 j j 节点,且经过的特殊边系数之和为 k k ,至少所需的最小距离。
于是,出现负环的条件变成了 kx+f[n][i][k]<jx+f[n−1][i][j] k x + f [ n ] [ i ] [ k ] < j x + f [ n − 1 ] [ i ] [ j ] 。
易知,一个负权环影响的区域是整个联通块。
因此,对于每个联通块中的点,可行的 x x 需要满足的条件为:
mini,k{kx+f[n][i][k]}≥mini,j{jx+f[n][i][j]} min i , k { k x + f [ n ] [ i ] [ k ] } ≥ min i , j { j x + f [ n ] [ i ] [ j ] }
解这个不等式即可~
怎么解?
首先,对于每个 k k ,枚举所有 j j ,求出下式的解集的补集:
kx+f[n][i][k]≥minj{jx+f[n][i][j]} k x + f [ n ] [ i ] [ k ] ≥ min j { j x + f [ n ] [ i ] [ j ] }
然后对于得到的所有补集,并在一起并取个补集即可~
代码:
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
#define f(i,j,k) f[i][j][k+105]
typedef double db;
typedef long long ll;
typedef pair<ll,ll> pr;
const ll N=109;
const ll M=N*N;
const ll Inf=1e18;
ll n,m;
struct o{ll u,v,w,s;}e[M];
vector<pr> vec[N],stk;
ll f[N][N][N<<1];
bool g[N][N];
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
return x*f;
}
inline void chkmin(ll &a,ll b){if(a>b)a=b;}
inline void chkmax(ll &a,ll b){if(a<b)a=b;}
int main()
{
n=read();m=read();
for(ll i=1;i<=m;i++)
{
e[i].u=read();e[i].v=read();
e[i].w=read();e[i].s=read();
g[e[i].u][e[i].v]=1;
}
for(ll i=1;i<=n;i++)
g[i][i]=1;
for(ll k=1;k<=n;k++)
for(ll i=1;i<=n;i++)
for(ll j=1;j<=n;j++)
g[i][j]|=(g[i][k]&g[k][j]);
for(int i=0;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=-n;k<=n;k++)
f(i,j,k)=Inf;
f(0,1,0)=0;
for(ll i=1;i<=n;i++)
{
memcpy(f[i],f[i-1],sizeof(f[i]));
for(ll j=1;j<=m;j++)
{
ll u=e[j].u,v=e[j].v,w=e[j].w,s=e[j].s;
for(ll k=-n;k<=n;k++)
if(f(i-1,u,k)<Inf)
chkmin(f(i,v,k+s),f(i-1,u,k)+w);
}
}
for(ll i=1;i<=n;i++)
for(ll k=-n;k<=n;k++)
if(f(n,i,k)!=Inf)
{
ll l=-Inf,r=Inf;
for(ll j=-n;j<=n;j++)
if(f(n-1,i,j)!=Inf)
{
if(k>j)
chkmin(r,(ll)ceil(((db)f(n-1,i,j)-f(n,i,k))/((db)k-j)));
else if(k<j)
chkmax(l,(ll)floor(((db)f(n-1,i,j)-f(n,i,k))/((db)k-j)));
else if(f(n,i,k)>=f(n-1,i,j))
goto hell;
}
if(l<r)
vec[i].push_back(pr(l,r));
hell:;
}
for(ll i=1;i<=n;i++)
{
stk.clear();
for(ll j=1;j<=n;j++)
if(g[1][j] && g[j][i])
{
for(ll k=0;k<vec[j].size();k++)
stk.push_back(vec[j][k]);
}
sort(stk.begin(),stk.end());
ll l=Inf,r=-Inf,lst=-Inf;
for(ll j=0;j<stk.size();j++)
{
if(!j && -Inf<stk[j].first)
{
l=-Inf,r=stk[j].first;
goto heaven;
}
if(lst!=-Inf && lst<=stk[j].first)
{
l=lst;r=stk[j].first;
goto heaven;
}
chkmax(lst,stk[j].second);
}
if(lst<Inf)l=lst,r=Inf;
heaven:;
if(l==-Inf || r==Inf || !stk.size())
puts("-1");
else
{
ll ans=max(r-l+1,0ll);
printf("%lld\n",ans);
}
}
return 0;
}