【HAOI2014】走出金字塔

时间:2024-04-17 00:16:51

神奇……

原题:

在探险的过程中,考古学家Dr. Kong 无意地被困在一个金字塔中。金字塔中的每个房间都是三角形。Dr. Kong可以破壁走到相邻的房间去。
【HAOI2014】走出金字塔

例如,如果他目前处于三角形(2,2)房间,那么他可以破壁走到三角形(2,1)、(2,3)或(1,1)房间。但破壁一面墙需要花费K分钟时间,而考古学家Dr. Kong 的体能只能支持他到S分钟。
好在Dr. Kong手中有这个金字塔地图,他发现金字塔有许多出口,一旦他进入一个有出口的三角形房间,他再用1分钟就可以走出金字塔。
【HAOI2014】走出金字塔
现在,你能否帮助Dr. Kong找到一个走出金字塔花费时间最少的出口?若能,输出Dr. Kong走出金字塔后还剩下的体能时间(应当大于或等于0);若不能,输出-1。

1 <= N <= 1000000   0<=M<=10000  0<K<=20  10<=S<=10000

这题第一眼,诶spfa搞一搞,但是建图点数是O(n^2)的,n<=1e6搞不了

但是拆每个墙的花费是固定的,所以是不是可以搞个奇奇怪怪的计算公式?

那就要找规律了,先画个图,一个三层的金字塔大概是酱紫的

【HAOI2014】走出金字塔

然后画一下连边的关系,再对齐,差不多就是酱紫

【HAOI2014】走出金字塔

画个四层的更好推规律

因为是无向边,所以把从坐上到右下和从右下到坐上放到一起考虑,左下到右上和右上到左下同理

然后可以发现,从(x,y)到(x,y+1)都要走两格,并且从坐上到右下可以顺着斜边走,使x和y都增加

如果从左下到右上就要逆着斜边上去,这个时候显然最优方案是每次话2条边上去,然后平着走

顺着斜边走的话有点麻烦,我开始的做法是max(abs(y2-y1),abs(x2-x1)*2),后来的做法是max(abs(y2-y1),abs(x2-x1)*2)-((x1&1)==(x2&1) && abs(y1-y2)&1);(考虑(1,1)到(3,4)的情况

证明就不证了,这题太玄了……

(至于我怎么想出来这个表达式的?面向数据编程一,一
果然我画的图的确没有卵用吗 _(:3 」∠)_

代码:

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int oo=;
int rd(){int z=,mk=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')mk=-; ch=getchar();}
while(ch>=''&&ch<=''){z=(z<<)+(z<<)+ch-''; ch=getchar();}
return z*mk;
}
int n,m,k,s;
int sx,sy,tx,ty;
int mn=oo;
int cclt(int x1,int y1,int x2,int y2){
if(x1<x2 ^ y1<y2) return abs(y2-y1)+abs(x2-x1)*;
else return max(abs(y2-y1),abs(x2-x1)*)-((x1&)==(x2&) && abs(y1-y2)&);
}
int main(){//freopen("ddd.in","r",stdin);
cin>>n>>m>>k>>s;
cin>>sx>>sy;
for(int i=;i<=m;++i){
tx=rd(),ty=rd();
mn=min(mn,cclt(sx,sy,tx,ty));
}
mn=s-mn*k-;
cout<<(mn>=?mn:-)<<endl;
return ;
}