拆点:虫洞
题目描述
N个虫洞,M条单向跃迁路径。
从一个虫洞沿跃迁路径到另需要消耗定量的燃料和 1单位时间。
虫洞有白和黑之分。
设一条跃迁路径两端的虫洞质量差为 delta 。
从白洞跃迁到黑,消耗的燃料值减少delta。 若该条路径消耗的燃料值变为负数话,取0。
从黑洞跃迁到白,消耗的燃料值增加delta。
路径两端均为黑洞或白,消耗的燃料值不变化。
作为压轴题,自然不会是如此简单的最短路问所以每过1单位时间黑洞变为白,单位时间黑洞变为白,变为黑洞。
在飞行过程中,可以选择一个虫洞停留1个单位时间,如果当前为白洞则不消耗燃料个单位时间,如果当前为白洞则不消耗燃料否则消耗s[i]的燃料。
现在请你求出从虫洞1到N最少的燃料消耗,保证一定存在最少的燃料消耗,保证一定存在1到N的路线。
输入格式第 1行: 2个正整数 N,M
第 2行: N个整数,第 i个为 0表示虫洞 表示虫洞 i开始时为白洞, 1表示黑洞。 表示黑洞。
第 3行: N个整数,第 i个数表示虫洞 i的质量 w[i]w[i] 。
第 4行: N个整数,第 i个数表示在虫洞 i停留消耗的燃料 s[i] 。
第 5..M+4 行 :每行 :每3个整数 u,v,k 表示在没有影响的情况下,从虫洞u到虫洞v需要消耗燃料需要消耗燃料 k。
输出格式一个整数,表示最少的燃料消耗
输入样例
4 5
1 0 1 0
10 100 10 10
5 20 15 10
1 2 30
2 3 40
1 3 20
1 4 200
3 4 200
输出样例
130
数据范围
对于 30% 的数据: 1<=N<=100,1<=M<=500
对于 60% 的数据: 1<=N<=1000,1<=M<=5000
对于 100% 的数据:1<=N<=5000 1<=M<=30000, 1<=u,v<=N, k,w[i],s[i]<=200
其中 20%的数据为 1<=N<=3000 的链
样例解释 1->3 ->4
每个点有两种状态,白洞、黑洞
若由白洞到黑洞,到达黑洞时,黑洞已变为白洞。
若由黑洞到白洞,到达白洞时,白洞已变为黑洞。
所以,可以将一个点拆分为两个点,分别在两个平面内,上层为原来的状态,下层为相反的状态。
若有边i,j,权值为t,从上层i到下层j连一条边,从下层i到上层j连一条边。
且由上层i到下层j连一条边。
权值分别计算。
起点为上层1,最后结果为下层n,上层n的最小值。
通过spfa计算。
可以采用循环队列。
code:
var n,m:longint;
w,s,stay:array[1..10000]of longint;
edge:array[1..100000]of record
x,y,z:longint;
end;
num1,num2:array[1..10000]of longint;
i,j,k:longint;
num:longint;
x,y,z:longint;
queue:array[0..100000]of longint;
dist:array[1..10000]of longint;
head,tail:longint;
ans:longint;
used:array[1..10000]of boolean;
function max(x,y:longint):longint;
begin if x>y
then exit(x)
else exit(y);
end;
function min(x,y:longint):longint;
begin if x>y
then exit(y)
else exit(x);
end;
procedure qsort(x,y:longint);
var head,tail,k,temp:longint;
begin head:=x; tail:=y;
k:=edge[(head+tail) div 2].x;
while head<tail do
begin while edge[head].x<k do inc(head);
while k<edge[tail].x do dec(tail);
if head<=tail
then begin temp:=edge[head].x;
edge[head].x:=edge[tail].x;
edge[tail].x:=temp;
temp:=edge[head].y;
edge[head].y:=edge[tail].y;
edge[tail].y:=temp;
temp:=edge[head].z;
edge[head].z:=edge[tail].z;
edge[tail].z:=temp;
inc(head);
dec(tail);
end;
end;
if head<y then qsort(head,y);
if x<tail then qsort(x,tail);
end;
begin
readln(n,m);
for i:=1 to n do
read(s[i]);
for i:=1 to n do
read(w[i]);
for i:=1 to n do
read(stay[i]);
num:=0;
for i:=1 to m do
begin readln(x,y,z);
inc(num);
edge[num].x:=x;
edge[num].y:=y+n;
if s[x]=s[y]
then edge[num].z:=z;
if (s[x]=1)and(s[y]=0)
then edge[num].z:=z+abs(w[x]-w[y]);
if (s[x]=0)and(s[y]=1)
then edge[num].z:=max(0,z-abs(w[x]-w[y]));
inc(num);
edge[num].x:=x+n;
edge[num].y:=y;
if s[x]=s[y]
then edge[num].z:=edge[num-1].z;
if (s[x]=1)and(s[y]=0)
then edge[num].z:=max(0,z-abs(w[x]-w[y]));
if (s[x]=0)and(s[y]=1)
then edge[num].z:=z+abs(w[x]-w[y]);
end;
for i:=1 to n do
begin if s[i]=1
then begin inc(num);
edge[num].x:=i;
edge[num].y:=i+n;
edge[num].z:=stay[i];
inc(num);
edge[num].x:=i+n;
edge[num].y:=i;
edge[num].z:=0;
end;
if s[i]=0
then begin inc(num);
edge[num].x:=i;
edge[num].y:=i+n;
edge[num].z:=0;
inc(num);
edge[num].x:=i+n;
edge[num].y:=i;
edge[num].z:=stay[i];
end;
end;
qsort(1,num);
for i:=1 to 2*n do
begin num1[i]:=maxlongint;
num2[i]:=0;
end;
for i:=num downto 1 do
begin inc(num2[edge[i].x]);
if i<num1[edge[i].x]
then num1[edge[i].x]:=i;
end;
fillchar(used,sizeof(used),true);
for i:=1 to 2*n do
dist[i]:=maxint*500;
head:=0; tail:=0;
queue[head]:=1;
dist[1]:=0;
while head<>tail+1 do
begin for i:=1 to num2[queue[head]] do
begin if (dist[queue[head]]+edge[num1[queue[head]]+i-1].z<
dist[edge[num1[queue[head]]+i-1].y])
then begin dist[edge[num1[queue[head]]+i-1].y]:=
dist[queue[head]]+edge[num1[queue[head]]+i-1].z;
if (used[edge[num1[queue[head]]+i-1].y])
then begin tail:=(tail+1) mod (2*n) ;
queue[tail]:=edge[num1[queue[head]]+i-1].y;
used[edge[num1[queue[head]]+i-1].y]:=false;
end;
end;
end;
used[queue[head]]:=true;
head:=(head+1) mod (2*n);
end;
ans:=min(dist[n],dist[2*n]);
writeln(ans);
end.