【蓝桥杯】每日练习 Day6

前言

今天只有一道题目(每天上课真的好累啊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;
}