2017 计蒜之道 初赛 第六场(个人题解)

 A. 微软手机的信号显示

模拟题,用一个7*7个数组,然后int除法判断信号有几成就可以了

代码:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 10;
char s[N][N];

int main(void)
{
    int i;
    CLR(s, ' ');
    for (i = 1; i <= 6; ++i)
        s[i][1] = s[i][7] = '|', s[i][8] = '\0';
    strcpy(s[1] + 1, "+-----+");
    strcpy(s[7] + 1, "+-----+");
    int d;
    scanf("%d", &d);
    int b = d / 10;
    if (b >= 9)
    {
        s[2][6] = 'G';
        s[2][5] = '4';
    }
    else if (b >= 6)
    {
        s[2][6] = 'G';
        s[2][5] = '3';
    }
    else
        s[2][6] = 'E';
    int cur = 2;
    int ge = d / 20;
    while (ge--)
    {
        if (cur == 2)
        {
            s[2][2] = '-';
        }
        if (cur == 3)
        {
            s[3][2] = '-';
            s[3][3] = '-';
        }
        if (cur == 4)
        {
            s[4][2] = '-';
            s[4][3] = '-';
            s[4][4] = '-';
        }
        if (cur == 5)
        {
            s[5][2] = '-';
            s[5][3] = '-';
            s[5][4] = '-';
            s[5][5] = '-';
        }
        if (cur == 6)
        {
            s[6][2] = '-';
            s[6][3] = '-';
            s[6][4] = '-';
            s[6][5] = '-';
            s[6][6] = '-';
        }
        ++cur;
    }
    for (i = 1; i <= 7; ++i)
        puts(s[i] + 1);
    return 0;
}

  B. 微软大楼设计方案(简单)

模拟题+BFS,我是用一个比较大的数组当地图,然后以80行为地面根据楼的高度一层一层地往上盖楼房,然后$O(m^2)$枚举两两点之间BFS出距离判断一下更新答案

代码:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 110;
int pos[N][N];
struct info
{
    int x, y, step;
};
info S, T;
int H[N];
int vis[N][N];
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};

int check(info w)
{
    return !vis[w.x][w.y] && pos[w.x][w.y];
}
int bfs(info s, info t)
{
    CLR(vis, 0);
    queue<info>Q;
    vis[s.x][s.y] = 1;
    s.step = 0;
    Q.push(s);
    while (!Q.empty())
    {
        info u = Q.front();
        Q.pop();
        if (u.x == t.x && u.y == t.y)
            return u.step;
        for (int i = 0; i < 4; ++i)
        {
            info v = u;
            v.x += dir[i][0];
            v.y += dir[i][1];
            v.step++;
            if (check(v))
            {
                Q.push(v);
                vis[v.x][v.y] = 1;
            }
        }
    }
    return INF;
}
int main(void)
{
    int n, m, i, k, j;
    scanf("%d%d", &n, &k);
    for (i = 1; i <= n; ++i)
    {
        scanf("%d", &H[i]);
        for (j = 0; j < H[i]; ++j)
            pos[80 - j][i] = 1;
    }
    info P[N];
    scanf("%d", &m);
    for (i = 0; i < m; ++i)
    {
        scanf("%d%d", &P[i].y, &P[i].x);
    }
    int cnt = 0;
    for (i = 0; i < m; ++i)
    {
        for (j = i + 1; j < m; ++j)
        {
            S = P[i], T = P[j];
            S.x = 80 - S.x + 1;
            T.x = 80 - T.x + 1;
            cnt += (bfs(S, T) <= k);
        }
    }
    printf("%d\n", cnt);
    return 0;
}

  C. 微软大楼设计方案(中等)

RMQ+分类讨论,这题一开始可以发现实际上还是可以$O(m^2)$暴力枚举两个点然后算距离,但是这样一来距离的计算基本得$O(1)$才行,然后又发现楼是一层一层地往上盖的,任意两栋楼之间至少底层可以通过,因此两个点之间的距离只跟两个点之间的最低的楼房高度有关系,那么就用RMQ或者线段树预处理出区间最小值,然后每一次查询两个点之间的楼房最低高度,这里显然有四种情况,(左边小于等于和大于两种*右边小于等于和大于两种),实际上后面搞清楚的话有几种情况是一样的,然后根据情况算距离即可。

代码:

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 200010;
const int M = 2010;

int H[N];
struct info
{
    int x, y;
};
info P[M];
int Min[N][19];

void RMQ(int l, int r)
{
    int i, j;
    for (i = l; i <= r; ++i)
        Min[i][0] = H[i];
    for (j = 1; l + (1 << j) - 1 <= r; ++j)
        for (i = l; i + (1 << j) - 1 <= r; ++i)
            Min[i][j] = min(Min[i][j - 1], Min[i + (1 << (j - 1))][j - 1]);
}
int ST(int l, int r)
{
    int len = r - l + 1;
    int k = 0;
    while ((1 << (k + 1)) <= len)
        ++k;
    return min(Min[l][k], Min[r - (1 << k) + 1][k]);
}
int main(void)
{
    int n, m, k, i, j;
    scanf("%d%d", &n, &k);
    for (i = 1; i <= n; ++i)
    {
        scanf("%d", &H[i]);
    }
    RMQ(1, n);
    scanf("%d", &m);
    for (i = 1; i <= m; ++i)
        scanf("%d%d", &P[i].y, &P[i].x);
    int ans = 0;
    for (i = 1; i <= m; ++i)
    {
        for (j = i + 1; j <= m; ++j)
        {
            int dx;
            int belonga = P[i].y;
            int belongb = P[j].y;
            if (belonga > belongb)
                swap(belonga, belongb);
            if (belonga == belongb)
                dx = abs(P[i].x - P[j].x);
            else
            {
                int low = ST(belonga, belongb);
                if (low >= P[i].x && low >= P[j].x)
                    dx = abs(belonga - belongb) + abs(P[i].x - P[j].x);
                else if (low >= P[i].x && low < P[j].x)
                    dx = abs(belonga - belongb) + abs(P[i].x - P[j].x);
                else if (low < P[i].x && low >= P[j].x)
                    dx = abs(belonga - belongb) + abs(P[i].x - P[j].x);
                else if (low < P[i].x && low < P[j].x)
                {
                    int xa = P[i].x, xb = P[j].x;
                    if (xa > xb)
                        swap(xa, xb);
                    dx = xa - low + xb - low + belongb - belonga;
                }
            }
            if (dx <= k)
                ++ans;
        }
    }
    printf("%d\n", ans);
    return 0;
}

 D. 微软大楼设计方案(困难)

RMQ+二分+一点思维,这题比赛的时候没写出来,因为想的方法就不对,以为直接从第一个点算出到其他点的距离Dis[i],然后任意两点间的最短距离就是Dis[i]-Dis[j],实际上有很多情况下这种做法是错的,原因就在于不符合最短路的性质(即使有楼层是从下往上盖的),甚至有时候实际距离还是这两者相加……

网上的一种做法是对x排序,即按点所在办公楼从左到右排序,然后用最坏的情况二分出最远一定能到达的点,那么什么是最坏的情况呢,假设当前有两个点在a号楼的最顶层和b号楼的最顶层,最坏情况就是a楼与b楼都是20层,那么a到b的距离就是$20-1+abs(a-b)+20-1=abs(a-b)+38$,如果记abs(a-b)为Δ,那么移项并合并可以得到Δ<=k-38,那么二分的时候用两者楼号之差的绝对值判断即可,然后由于这是最坏情况下的二分,很可能二分的范围之后还有一些点也是符合题目条件的,因此用最好的情况进行遍历,即假设当前这个点在第一层,那么可以到达最远的楼层为当前点所在楼号$x_{cur}+k$,如果某个点的x大于$x_{cur}+k$,那么就算这两个点都在第一层只需要左右移动这种最好情况下也是无法到达的,更不用说其他还要上下楼的情况了,如果出现这种情况就可以break了。

代码:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair<int, int> pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 200010;
struct info
{
    int x, y;
    bool operator<(const info &rhs)const
    {
        return x < rhs.x;
    }
};
info P[N];
int H[N], low[N][19];
int n, m, k;

void RMQinit(int l, int r)
{
    int i, j;
    for (i = l; i <= r; ++i)
        low[i][0] = H[i];
    for (j = 1; l + (1 << j) - 1 <= r; ++j)
    {
        for (i = l; i + (1 << j) - 1 <= r; ++i)
        {
            low[i][j] = min(low[i][j - 1], low[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int ST(int l, int r)
{
    int len = r - l + 1;
    int k = 0;
    while ((1 << (k + 1)) <= len)
        ++k;
    return min(low[l][k], low[r - (1 << k) + 1][k]);
}
int getpos(int l, int r, info t)
{
    int ans = l;
    while (l <= r)
    {
        int mid = MID(l, r);
        int L = t.x, R = P[mid].x;
        int dx = R - L;
        if (dx <= k - 38)
        {
            l = mid + 1;
            ans = mid;
        }
        else
            r = mid - 1;
    }
    return ans;
}
int main(void)
{
    int i, j;
    scanf("%d%d", &n, &k);
    for (i = 1; i <= n; ++i)
        scanf("%d", &H[i]);
    RMQinit(1, n);

    scanf("%d", &m);
    for (i = 1; i <= m; ++i)
        scanf("%d%d", &P[i].x, &P[i].y);
    sort(P + 1, P + 1 + m);

    LL ans = 0LL;
    for (i = 1; i <= m; ++i)
    {
        int far = getpos(i, m, P[i]);
        ans += (LL)(far - i);
        for (j = far + 1; j <= m; ++j)
        {
            int L = P[i].x, R = P[j].x;
            int Low = ST(L, R);
            int dx = R - L + P[i].y - Low + P[j].y - Low;
            if (dx <= k)
                ++ans;
            else if (R - L > k)
                break;
        }
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2017-06-05 17:57  Blackops  阅读(232)  评论(0编辑  收藏  举报