AtCoder Beginner Contest 221 D Online games

Online games

https://atcoder.jp/contests/abc221/tasks/abc221_d

题目大意:给出 n n n 个人的区间,表示这人在这个区间时间内会登录游戏,现在请输出 n n n 个数 k i k_i ki ,表示有恰好 k i k_i ki 个人登录游戏的天数。

这种多区间和交并有点关系的问题就可以往扫面线的思想去靠一靠。那么我们利用扫描线的思想,处理出每个变化点,也就是 a i a_i ai 点此时区间会加一, a i + b i a_i+b_i ai+bi 点此时区间会减一。那么我们现在假定两个相邻变化点 i i i j j j ,我们会发现 [ i , j ) [i,j) [i,j) 的登录总人数是不会变的,因为这中间没有任何变化点,登录人数不会变化。所以我们只要将变化点排序,然后去遍历变化点 i i i ,维护到此变化点 i i i 的时候尚存的区间个数(登录总人数) c n t cnt cnt ,再利用与后一个相邻点 j j j 的距离更新答案。注意遍历变化点的时候最后最后一个变化点不需要遍历到,因为它没有后一个相邻点。这样实际上将所有有人登录的区间都计算到了,所以最后输出答案即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 4e5 + 10;

int n, len, ans[N];

pair<int, int> seg[N];

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w" ,stdout);
#endif
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int l, r; scanf("%d%d", &l, &r);
        seg[++len] = {l, 1};
        seg[++len] = {l + r, -1};
    }
    sort(seg + 1, seg + 1 + len);
    int cnt = 0;
    for (int i = 1; i < len; ++i) {
        cnt += seg[i].second;
        ans[cnt] += seg[i + 1].first - seg[i].first;
    }
    for (int i = 1; i <= n; ++i) {
        printf("%d%c", ans[i], i == n ? '\n' : ' ');
    }
}

一开始其实是脑瘫码了个线段树的。。。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 4e5 + 10;

int n, len, t[N], ans[N];

ll sum[N << 5], lz[N << 5];
int lson[N << 5], rson[N << 5], root, tot;

pair<int, int> seg[N];

void update(int &rt, int l, int r, int L, int R) {
    if (!rt) rt = ++tot;
    sum[rt] += min(r, R) - max(l, L) + 1;
    if (L <= l && r <= R) {
        lz[rt]++;
        return ;
    }
    int mid = (1ll* l + r) >> 1ll;
    if (mid >= L) update(lson[rt], l, mid, L, R);
    if (mid < R) update(rson[rt], mid + 1, r, L, R);
}

ll query(int rt, int l, int r, int L, int R) {
    if (!rt) return 0;
    if (L <= l && r <= R) return sum[rt];
    ll ans = (min(r, R) - max(l, L) + 1) * lz[rt];
    int mid = (1ll * l + r) >> 1ll;
    if (mid >= L) ans += query(lson[rt], l, mid, L, R);
    if (mid < R) ans += query(rson[rt], mid + 1, r, L, R);
    return ans;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d%d", &seg[i].first, &seg[i].second);
        t[++len] = seg[i].first; t[++len] = seg[i].first + seg[i].second - 1;
        update(root, 1, 2e9, seg[i].first, seg[i].first + seg[i].second - 1);
    }
    sort(t + 1, t + 1 + len);
    len = unique(t + 1, t + 1 + len) - t - 1;
    ans[query(root, 1, 2e9, t[1], t[1])]++;
    for (int i = 2; i <= len; ++i) {
        int L = t[i - 1] + 1, R = t[i] - 1;
        if (L <= R) {
            ans[query(root, 1, 2e9, L, R) / (R - L + 1)] += R - L + 1;
        }
        ans[query(root, 1, 2e9, t[i], t[i])]++;
    }
    for (int i = 1; i <= n; ++i) {
        printf("%d%c", ans[i], i == n ? '\n' : ' ');
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值