前言
今天只有一道题目(每天上课真的好累啊QAQ)。
周末周末快快来。
沿栅栏散步
分析
总共有N
头奶牛。
每个柱子必定在拐点处,并且柱子的个数是偶数,这是肯定的,因为最终要组成一个环。
初始给出q
个柱子。相邻的两个柱子之间是栅栏。
总共N
次询问,每次给出起点和终点,询问最小距离。
x, y <= 1000
且点很多,使用邻接矩阵存储。
离线问题 + 询问次数很大,考虑打表。
那么,如何来打呢?
分析起点到终点的行走路线,不难想到对于每种方案必定只有两种(重合的除外),即:顺时针和逆时针。
先考虑如何将环变成区间,使用破环成链的思想,设置一个起点和终点,随后以逻辑或双倍长度的方式模拟成环。
随后可以发现每次询问其实求的是一个区间,考虑使用前缀和。
我们注意到不一定所有的牛初始都在柱子上,但是一定是在栅栏上,如何处理呢?
其实我们可以将位置看成是柱子,x,y <= 1000
,即位置的总数在1e6
以内,空间上完全充足,所以我们可以直接将牛可能出现的位置和柱子一样存储,一并编号处理。
随后我们就可以发现相邻的两个位置距离一定为1
,并且不相交,我们的编号的步长也是1
,所以我们可以直接在存储地图的数组上进行前缀和操作。无需再开多余的数组,用逻辑来模拟出环即可。
代码
/*
无需前缀和
无需破环成链
无需数组映射
只需要一个map!!!
*/
#include<iostream>
using namespace std;
const int N = 1010, Q = 200010, S = 2e6 + 10;
int n, q, cnt;
int map[N][N];
int x, y, x0, y0;
void lian(int x, int y, int x1, int y1)
{ //前指向后---
int a = (x1 - x) + (y1 - y) > 0 ? 1 : -1;
if(x == x1)
{
for(y += a; y != y1; y += a)
map[x][y] = ++cnt;
}
else
{
for(x += a; x != x1; x += a)
map[x][y] = ++cnt;
}
}
int main()
{
cin >> n >> q;
for(int i = 1; i <= q; i++)
{
int x1, y1; cin >> x1 >> y1;
if(i >= 2) lian(x, y, x1, y1);
else x0 = x, y0 = y;
map[x1][y1] = ++cnt;
x = x1; y = y1;
}
lian(x, y, x0, y0);
while(n--)
{
cin >> x0 >> y0 >> x >> y;
int l1 = map[x0][y0], l2 = map[x][y];
if(l1 > l2) swap(l1, l2);
cout << min(l2 - l1, l1 + cnt - l2) << '\n';
}
return 0;
}