CCNU 随便出的练习赛

http://vjudge.net/contest/136710#overview
http://vjudge.net/contest/136710#rank

这是一场 FQ 出的练习赛,FQ 表示大多都是做过的题目。如果有哪个题目明显有印象但是没能做出来的,应该警惕。

三省吾身:传不习乎?

当然我认为此场对代码能力要求比较大,应该注意提高编程速度和准确度。

A

问一个数是否可以拆成两个正偶数的和。

puts(w % 2 == 0 && w >= 4 ? "YES" : "NO");

B

经典问题,求最大字段和。

贪心即可。

#include <bits/stdc++.h>

int main()
{
    int __;
    scanf("%d", &__);
    for (int _ = 1; _ <= __; ++_) {
        int n;
        scanf("%d", &n);
        int sum = 0;
        int max_sum = -(1 << 30);
        int begin, end, k = 1;
        for (int i = 1; i <= n; ++i) {
            int xi;
            scanf("%d", &xi);
            sum += xi;
            if (sum > max_sum) {
                max_sum = sum;
                begin = k;
                end = i;
            }
            if (sum < 0) {
                sum = 0;
                k = i + 1;
            }
        }
        printf("Case %d:\n", _);
        printf("%d %d %d\n", max_sum, begin, end);
        if (_ != __) {
            puts("");
        }
    }
    return 0;
}

C

求最短路径,墙在时间是 k 的倍数的时候消失。

同一个位置按照时间模 k 的值不同有 k 种状态,照此 bfs 即可,当目前到达时间比同一状态上一次到达时间段时才有必要继续搜索。

#include <bits/stdc++.h>

struct NODE
{
    int x;
    int y;
    int step;

    NODE(int _x = 0, int _y = 0, int _step = 0)
    { x = _x; y = _y; step = _step; }
};

const int MAX_N = 123;
const int MAX_K = 15;

char str[MAX_N][MAX_N];
int flag[MAX_N][MAX_N][MAX_K];

const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};

int bfs(int k, NODE you)
{
    std::queue<NODE> q;
    memset(flag, 0x3f, sizeof(flag));
    q.push(you);
    flag[you.x][you.y][0] = 0;
    for (; !q.empty(); ) {
        NODE now = q.front();
        q.pop();
        for (int i = 0; i < 4; ++i) {
            NODE nxt(now.x + dx[i], now.y + dy[i], now.step + 1);
            if (str[nxt.x][nxt.y] == 0) {
                // out of map
                continue;
            }
            if (nxt.step >= flag[nxt.x][nxt.y][nxt.step % k]) {
                // bad move
                continue;
            }
            if (str[nxt.x][nxt.y] == '#' && nxt.step % k) {
                // there is a stone
                continue;
            }
            q.push(nxt);
            flag[nxt.x][nxt.y][nxt.step % k] = nxt.step;
            if (str[nxt.x][nxt.y] == 'G') {
                return nxt.step;
            }
        }
    }
    return -1;
}

int main()
{
    int __;
    scanf("%d", &__);
    for (int _ = 1; _ <= __; ++_) {
        int r, c, k;
        scanf("%d %d %d", &r, &c, &k);
        NODE you;
        memset(str, 0, sizeof(str));
        for (int i = 1; i <= r; ++i) {
            scanf("%s", str[i] + 1);
            for (int j = 1; j <= c; ++j) {
                if (str[i][j] == 'Y') {
                    you.x = i;
                    you.y = j;
                }
            }
        }
        int answer = bfs(k, you);
        if (answer != -1) {
            printf("%d\n", answer);
        }
        else {
            printf("Please give me another chance!\n");
        }
    }
    return 0;
}

D

求可以被看到的方格序号。

方格很少,直接模拟即可。首先确定每个方格的位置 bi ,具体即是前面所有方格后接的位置的最大值。然后枚举方格,看是否被完全覆盖,具体只要注意只有大的方格会覆盖小的方格即可。

#include <bits/stdc++.h>

const int MAX_N = 100;

int b[MAX_N];
int s[MAX_N];

int main()
{
    int n;
    for (; scanf("%d", &n) == 1 && n; ) {
        scanf("%d", s + 1);
        b[1] = s[1];
        for (int i = 2; i <= n; ++i) {
            scanf("%d", s + i);
            b[i] = 0;
            for (int j = 1; j < i; ++j) {
                b[i] = std::max(b[i], b[j] + std::min(s[i], s[j]) * 2);
            }
        }
        bool first = true;
        for (int i = 1; i <= n; ++i) {
            int l = b[i] - s[i];
            int r = b[i] + s[i];
            for (int j = 1; j <= n; ++j) {
                if (i == j) {
                    continue;
                }
                if (s[j] <= s[i]) {
                    continue;
                }
                int x = b[j] - s[j];
                int y = b[j] + s[j];
                if (x <= l && l <= y) {
                    l = y;
                }
                if (x <= r && r <= y) {
                    r = x;
                }
            }
            if (l < r) {
                if (first) {
                    first = false;
                }
                else {
                    printf(" ");
                }
                printf("%d", i);
            }
        }
        printf("\n");
    }
    return 0;
}

E

经典问题,求源到每个点的最短路和加每个点到源的最短路之和。

前一部分可以用单源最短路的定势(dijkstra/spfa)解决;后一部分,只要把全部的边反向,再求单源最短路即可。

#include <bits/stdc++.h>

const int MAX_N = 1e6 + 10;
const int MAX_M = 2e6 + 10;

struct EDGE
{
    int to;
    int cost;
    int next;
} edge[MAX_M];

int head[2][MAX_N];
int edge_cnt;

void reset_edge(int n)
{
    edge_cnt = 0;
    int* head0 = head[0];
    int* head1 = head[1];
    for (int i = 1; i <= n; ++i) {
        head0[i] = head1[i] = -1;
    }
}

void add_edge_(int u, int v, int cost, int* chead)
{
    edge[edge_cnt] = (EDGE) {v, cost, chead[u]};
    chead[u] = edge_cnt++;
}

void add_edge(int u, int v, int cost)
{
    add_edge_(u, v, cost, head[0]);
    add_edge_(v, u, cost, head[1]);
}

void sssp(int src, int* chead, long long* dis)
{
    typedef std::pair<long long, int> pii;
    std::priority_queue<pii> pq;
    static bool done[MAX_N];

    memset(done, 0, sizeof(done[0]) * MAX_N);
    memset(dis, 0x3f, sizeof(dis[0]) * MAX_N);
    dis[src] = 0;
    pq.push(pii(0LL, src));

    for (; !pq.empty(); ) {
        pii p = pq.top();
        pq.pop();
        int u = p.second;
        if (done[u]) {
            continue;
        }
        done[u] = true;
        for (int i = chead[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            int c = edge[i].cost;
            if (dis[v] > c + dis[u]) {
                dis[v] = c + dis[u];
                pq.push(pii(-dis[v], v));
            }
        }
    }
}

int main()
{
    int __;
    scanf("%d", &__);
    for (int _ = 1; _ <= __; ++_) {
        int n, m;
        scanf("%d %d", &n, &m);
        reset_edge(n);
        for (int mi = 1; mi <= m; ++mi) {
            int u, v, c;
            scanf("%d %d %d", &u, &v, &c);
            add_edge(u, v, c);
        }
        static long long dis[MAX_N];
        long long sum = 0;
        sssp(1, head[0], dis);
        for (int i = 1; i <= n; ++i) {
            sum += dis[i];
        }
        sssp(1, head[1], dis);
        for (int i = 1; i <= n; ++i) {
            sum += dis[i];
        }
        printf("%lld\n", sum);
    }
    return 0;
}

F

给一个数列,求有多少最大值减最小值不超过 k 的区间。

几乎是单调队列的模板问题。当然也可以用支持快速查询区间最值的数据结构解决。

这里给出稀疏表的写法。枚举左端点,二分右端点——可以延伸到的最远位置。

#include <bits/stdc++.h>

const int MAX_N  = 1e5 + 10;
const int MAX_LOG_N = 20;

int a[MAX_N];
int stmax[MAX_LOG_N][MAX_N];
int stmin[MAX_LOG_N][MAX_N];
int uplog[MAX_N];

void initst(int n)
{
    int* now_max = stmax[0];
    int* now_min = stmin[0];
    int* pre_max;
    int* pre_min;
    for (int i = 1; i <= n; ++i) {
        now_max[i] = now_min[i] = a[i];
    }
    for (int j = 1, pj = 2; pj <= n; ++j, pj <<= 1) {
        now_max = stmax[j];
        pre_max = stmax[j - 1];
        now_min = stmin[j];
        pre_min = stmin[j - 1];
        int hpj = pj / 2;
        for (int i = 1; i + pj - 1 <= n; ++i) {
            now_max[i] = std::max(pre_max[i], pre_max[i + hpj]);
            now_min[i] = std::min(pre_min[i], pre_min[i + hpj]);
        }
    }
    uplog[1] = 0;
    for (int i = 2; i <= n; ++i) {
        uplog[i] = uplog[i >> 1] + 1;
    }
}

int rmaxq(int l, int r)
{
    int p = uplog[r - l + 1];
    return std::max(stmax[p][l], stmax[p][r - (1 << p) + 1]);
}

int rminq(int l, int r)
{
    int p = uplog[r - l + 1];
    return std::min(stmin[p][l], stmin[p][r - (1 << p) + 1]);
}

int extend(int i, int n, int k)
{
    if (rmaxq(i, n) - rminq(i, n) < k) {
        return n;
    }
    int low = i;
    int upp = n;
    for (; upp - low > 1; ) {
        int mid = (low + upp) >> 1;
        if (rmaxq(i, mid) - rminq(i, mid) < k) {
            low = mid;
        }
        else {
            upp = mid;
        }
    }
    return low;
}

int main()
{
    int __;
    scanf("%d", &__);
    for (int _ = 1; _ <= __; ++_) {
        int n, k;
        scanf("%d %d", &n, &k);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", a + i);
        }
        initst(n);
        long long answer = 0;
        for (int i = 1; i <= n; ++i) {
            answer += extend(i, n, k) - i + 1;
        }
        printf("%lld\n", answer);
    }
    return 0;
}

posted by 张静之

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值