Codeforces Round #310 (Div. 1) A B C D题

47 篇文章 0 订阅
42 篇文章 0 订阅

本题解包含题目:


555A
555B
555C
555D


A题:

n(2*10^5)个大小不同的套娃  按照小的能套在大的里面的规则  已经按输入顺序套好了m个  问  最少拆、套几次可以形成1在2里 2在3里……n-1在n里  这种情况

思路:

我们可以这样理解一次操作——将大套娃拆开取出里面、套上一个大号的套娃  很明显每一步都是针对当前要处理的套娃中最大的那个进行的  那么最终状态一定是找到最长的1…x连续序列  除了这个序列外所有套娃全拆开  再按顺序套好

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010

int n, m, now;

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int p;
        scanf("%d", &p);
        for (int j = 1; j <= p; j++) {
            int x;
            scanf("%d", &x);
            if (x == j) now++;
        }
    }
    printf("%d\n", n - now - m + 1 + n - now);
    return 0;
}

B题:

数轴上有n(2*10^5)个不相交的区间和m(2*10^5)个桥  每座桥可以连接相邻两个区间[a,b]和[c,d]当且仅当桥长l<=d-a且l>=c-b  每座桥最多使用一次  求  建桥的方案

思路:

两个相邻区间可建桥的桥长范围是[c-b,d-a]  将这些范围和桥排序  很明显对于一座桥  要么不使用  要么就应该把它分配给  满足桥长范围约束条件下(在[c-b,d-a]范围内)  可容纳最长桥长(d-a)最小  的那个位置  那么用堆维护就好了

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010

int n, m, cnt;
int ans[N];

struct bridge {
    LL l;
    int id;

    bool operator<(const bridge ff) const {
        return l < ff.l;
    }
} b[N];

struct section {
    LL l, r, id;

    bool operator<(const section ff) const {
        return l < ff.l;
    }
} s[N];

struct node {
    int id;
    LL r;

    node() {

    }

    node(LL r, int id) {
        this->r = r;
        this->id = id;
    }

    bool operator<(const node ff) const {
        return r > ff.r;
    }
};
priority_queue<node> qu;

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%I64d%I64d", &s[i].l, &s[i].r);
    for (int i = 1; i < n; i++) {
        LL tmpl, tmpr;
        tmpr = s[i + 1].r - s[i].l;
        tmpl = s[i + 1].l - s[i].r;
        s[i].l = tmpl;
        s[i].r = tmpr;
        s[i].id = i;
    }
    sort(s + 1, s + n);
    for (int i = 1; i <= m; i++) {
        scanf("%I64d", &b[i].l);
        b[i].id = i;
    }
    sort(b + 1, b + m + 1);
    int idx = 1;
    for (int i = 1; i <= m; i++) {
        while (idx < n && s[idx].l <= b[i].l) {
            qu.push(node(s[idx].r, s[idx].id));
            idx++;
        }
        if (qu.empty()) continue;
        node x = qu.top();
        qu.pop();
        if (x.r < b[i].l) break;
        cnt++;
        ans[x.id] = b[i].id;
    }
    if (cnt == n - 1) {
        puts("Yes");
        printf("%d", ans[1]);
        for (int i = 2; i < n; i++) printf(" %d", ans[i]);
        puts("");
    } else puts("No");
    return 0;
}

C题:

n*n(10^9)的格子  按/方向对角线划开  保留左上部分  m(2*10^5)个询问  每次在/对角线上选一点  向左或向上延伸到最近的空格  并将路线上的格子变成空格  输出变成空格的格子数

思路:

很明显先离散化  我们将横向延伸和纵向延伸分开讨论

由于只有向左和向上两个方向  因此对于向左延伸  只需记录当前y坐标下  最大空格的x是多少  用x做差就是输出值  然后更新对应x区间在横向延伸上的y值记录  向上延伸同理

那么只需要建横纵两棵线段树  单点查询  区间修改  就好了

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010
#define L(x) (x<<1)
#define R(x) ((x<<1)|1)
#define MID(x,y) ((x+y)>>1)

int n, m;

struct ops {
    int x, y, to;
} op[N];
int x[N], y[N], vis[N];

struct tree {
    int l, r, lazy, near;
} f[2][N << 2];

void init(int l, int r, int i) {
    for (int j = 0; j < 2; j++) {
        f[j][i].l = l;
        f[j][i].r = r;
        f[j][i].near = f[j][i].lazy = 0;
    }
    if (l == r) return;
    int mid = MID(l, r);
    init(l, mid, L(i));
    init(mid + 1, r, R(i));
}

void down(int i, int j) {
    if (f[j][i].lazy) {
        f[j][L(i)].lazy = max(f[j][L(i)].lazy, f[j][i].lazy);
        f[j][L(i)].near = max(f[j][L(i)].near, f[j][i].lazy);
        f[j][R(i)].lazy = max(f[j][R(i)].lazy, f[j][i].lazy);
        f[j][R(i)].near = max(f[j][R(i)].near, f[j][i].lazy);
        f[j][i].lazy = 0;
    }
}

void update(int l, int r, int i, int j, int key) {
    if (f[j][i].l == l && f[j][i].r == r) {
        f[j][i].near = max(f[j][i].near, key);
        f[j][i].lazy = max(f[j][i].lazy, key);
        return;
    }
    down(i, j);
    int mid = MID(f[j][i].l, f[j][i].r);
    if (r <= mid) update(l, r, L(i), j, key);
    else if (l > mid) update(l, r, R(i), j, key);
    else {
        update(l, mid, L(i), j, key);
        update(mid + 1, r, R(i), j, key);
    }
}

int query(int p, int i, int j) {
    if (f[j][i].l == f[j][i].r) return f[j][i].near;
    down(i, j);
    int mid = MID(f[j][i].l, f[j][i].r);
    if (p <= mid) return query(p, L(i), j);
    else return query(p, R(i), j);
}

int main() {
    char UL[5];
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%s", &op[i].x, &op[i].y, UL);
        x[i] = op[i].x;
        y[i] = op[i].y;
        if (UL[0] == 'U') op[i].to = 0;
        else op[i].to = 1;
    }
    sort(x + 1, x + m + 1);
    sort(y + 1, y + m + 1);
    for (int i = 1; i <= m; i++) {
        op[i].x = lower_bound(x + 1, x + m + 1, op[i].x) - x;
        op[i].y = lower_bound(y + 1, y + m + 1, op[i].y) - y;
    }
    init(1, m, 1);
    for (int i = 1; i <= m; i++) {
        if (!vis[op[i].x]) {
            int to, tmpx, tmpy, ans;
            to = op[i].to;
            tmpx = (to) ? op[i].y : op[i].x;
            tmpy = (to) ? op[i].x : op[i].y;
            ans = query(tmpx, 1, to);
            printf("%d\n", (to) ? (x[tmpy] - x[ans]) : (y[tmpy] - y[ans]));
            vis[op[i].x] = 1;
            update(ans + 1, tmpy, 1, to^1, tmpx);
        } else printf("0\n");
    }
    return 0;
}

D题:

数轴上有n(2*10^5)个钉子  m(2*10^5)次询问  每次询问在某个钉子上用l长的绳子挂一重物并向右推重物  绳子开始旋转并且可能会碰到新的钉子  然后就绕新的钉子旋转  直到绳子绕一个钉子不停旋转为止  问  这个钉子的标号是几

思路:

除了模拟过程以外并不能想到什么好方法= =  当然模拟的过程中每次找新的钉子要二分去找  那么复杂度看似O(mnlogn)

但是随便想一下就知道  绝对不可能这么坏  旋转中心变换的区间越来越小  期望情况下每次能减少大约一半的钉子数  那么效率比O(mlognlogn)差一些是完全可以接受的  我写了一下  还是TLE在16组数据…

因为这里需要一个小优化  因为考虑到绳子很长  所以旋转中心很可能经常在x,y两个钉子上不停地相互交换  因此如果现在的旋转中心是y  上一次是x  下一次又要到x去  那么我们就计算绕圈圈的次数  这样可以快速缩小旋转中心变换的区间

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010

int n, m;
int x[N], y[N];

struct peg {
    int x, id;

    bool operator<(const peg ff) const {
        return x < ff.x;
    }
} tmp[N];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &x[i]);
        tmp[i].x = x[i];
        tmp[i].id = i;
    }
    sort(x + 1, x + n + 1);
    sort(tmp + 1, tmp + n + 1);
    for (int i = 1; i <= n; i++)
        y[tmp[i].id] = i;
    while (m--) {
        int i, l, to = 1, cnt = 0, last = -1;
        scanf("%d%d", &i, &l);
        i = y[i];
        while (true) {
            if (cnt == 2) {
                printf("%d\n", tmp[i].id);
                break;
            }
            if (to) {
                if (i + 1 > n || x[i + 1] - x[i] > l) {
                    cnt++;
                    to ^= 1;
                    continue;
                }
                cnt = 0;
                int pos = x[i] + l;
                int idx = upper_bound(x + 1, x + n + 1, pos) - x - 1;
                if (idx == last) {
                    if (l / (x[idx] - x[i]) % 2) {
                        l %= x[idx] - x[i];
                        last = i;
                        i = idx;
                        to ^= 1;
                    } else {
                        l %= x[idx] - x[i];
                    }
                } else {
                    l -= x[idx] - x[i];
                    last = i;
                    i = idx;
                }
            } else {
                if (i - 1 < 1 || x[i] - x[i - 1] > l) {
                    cnt++;
                    to ^= 1;
                    continue;
                }
                cnt = 0;
                int pos = x[i] - l;
                int idx = lower_bound(x + 1, x + n + 1, pos) - x;
                if (idx == last) {
                    if (l / (x[i] - x[idx]) % 2) {
                        l %= x[i] - x[idx];
                        last = i;
                        i = idx;
                        to ^= 1;
                    } else {
                        l %= x[i] - x[idx];
                    }
                } else {
                    l -= x[i] - x[idx];
                    last = i;
                    i = idx;
                }
            }
            to ^= 1;
        }
    }
    return 0;
}

PS:

博客沉寂了很久了  终于我又来刷题了~~~

上面看不懂的可以留言问我(毕竟语言表述能力有限= =  感觉自己写好差……)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值