[NOIP模拟] Work (树状数组)

[NOIP模拟] Work


题目背景

SOURCE:NOIP2015-SHY-5

题目描述

        假设现在离 noip 还有 m 天,有 n 个人要去参加比赛。他们每个人都有一个预定的训练量 r[i] ,所以每一天他们都抓紧时间练习。但是由于条件限制,第 i 天只有 t[i] 的时间可以练习。

        我们都知道,一个人在开始干活以前总要浪费一些时间做一些杂七杂八的事情。现在我们假定第 i 个人每天在训练前浪费的时间是固定的,记为 d[i] 。这段浪费掉的时间过后,选手会专心致志训练,他们会充分利用剩下的时间。然而一个可能的情况时,一个人还在无所事事的时候,某一天的训练时间已经过去了,所以他那一天什么事情都没有做。

        现在请问每个人在第几天的时候可以完成自己的训练任务。当然会存在志向远大但是很懒惰的人到最后也是做不完的情况。

输入格式

第一行两个整数 n m ,表示人数和天数 。
接下来一行 m 个整数 t[i]
接下来 n 行每行两个整数 d[i] r[i]

输出格式

一行输出 n 个整数表示每个人在第几天可以完成自己的工作,如果完不成,输出 0

样例数据 1

输入 

3 3
4 2 5
1 3
2 5
3 4

输出

1 3 0

备注

[ 数据范围 ]
30% 的输入数据 : 1n,m1000
100% 的输入数据 : 1n,m200000 1t[i]1000000 0d[i]1000000 1r[i]1000000

[ 注意事项 ]
如果某人浪费的时间超过一天,不需减去负的时间。

题解 :

    这道题,我在考场上打的主席树, 可是在 query 的时候搞忘打 root 了,这里写图片描述, 气到爆炸, 但是后面调了一下发现主席树 T 掉了, 其实我一直认为这个 T 的很玄学。
    接下来讲一下用树状数组的方法。 我们很容易发现前缀是满足单调的,于是我们只需要维护前缀, 然后二分。考虑维护前缀 :
    首先将 t 数组和 d 数组排序(降序), 然后对每一个 d 进行处理,我们将当前大于 d 的 t 读进树状数组, 因为 t 数组和 d 数组当前满足单调, 所以当前读进树状数组的 t, 一定对后面的 d 有贡献, 且当前小与 d 的 t一定对答案没有贡献, 于是我们只需要从头扫, 下一次接着上次的状态扫。 这样每个 t 最多扫过一次。并且求出了对答案的贡献, 对与树状数组, 我们都要求出,数值的前缀和有多少天有值,就有 now=query(x)queryday(x)d

代码 :

我加了主席树的代码

树状数组 复杂度 : nlog22(m)

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

inline int read() {
    int i = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1; ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
    }
    return i * f;
}

const int MAXN = 2e5 + 5;
const int MAXX = 1e6 + 5;
struct point {
    int num, pos;
    inline bool operator < (const point &x) const {
        return num > x.num;
    }
};
point t[MAXN], d[MAXN];
int r[MAXN], day[MAXN], ans[MAXN], n, m;
LL c[MAXN];

struct OUT {
    char buf[1048576];
    int pt;
    void putch(char ch) { buf[pt++] = ch; }
    OUT() : pt(0) {}
    ~OUT() { flush(); }
    void flush() { fwrite(buf, sizeof(char), pt, stdout); pt = 0; }
} out;

void put_int(int x) {
    char temp[20];
    int top = 0;
    if(x == 0) return out.putch('0');
    if(x < 0) out.putch('-'), x = -x;
    do temp[top++] = x % 10, x /= 10; while(x);
    do out.putch(temp[--top] + '0'); while(top);
}

void W(int x) {
    if(x < 0) putchar('-');
    if(x > 9) W(x / 10);
    putchar(x % 10 + '0');
}


inline int lowbit(int x) {
    return x & -x;
}

inline void insert(int x, int k) {
    for(int i = x; i < MAXN; i += lowbit(i)) c[i] += k, ++day[i];
}

inline LL query(int x) {
    LL res = 0;
    for(int i = x; i; i -= lowbit(i)) res += c[i];
    return res;
}

inline int query_day(int x) {
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) res += day[i];
    return res;
}

inline bool check(int x, int y) {
    LL now = query(x);//cout<<233<<'\n';
    int days = query_day(x);
    now -= d[y].num * days * 1ll;
    return now >= r[d[y].pos];
}

inline void solve() {
    int len = 1; 
    for(int i = 1; i <= n; ++i) {
        while(t[len].num > d[i].num && len <= m) insert(t[len].pos, t[len].num), ++len;
        int head = 1, tail = m, a = 0;
        while(head <= tail) {
            int mid = (head + tail) >> 1; //printf("%d\n", mid);
            if(check(mid, i)) a = mid, tail = mid - 1;
            else head = mid + 1;
        }
        //printf("%d\n", a);
        if(check(a, i)) ans[d[i].pos] = a; else ans[d[i].pos] = 0;
    }
    for(int i = 1; i <= n; ++i) put_int(ans[i]), out.putch(' ');
}

int main() {
    n = read(), m = read();
    for(int i = 1; i <= m; ++i) t[i].num = read(), t[i].pos = i;
    for(int i = 1; i <= n; ++i) d[i].num = read(), d[i].pos = i, r[i] = read();
    sort(t + 1, t + m + 1); sort(d + 1, d + n + 1);
    solve();
}

主席树 复杂度 : nlog2(m)log2(1e6)

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

inline int read() {
    int i = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1; ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        i = (i << 3) + (i << 1) + ch - '0'; ch = getchar();
    }
    return i * f;
}

const int MAXN = 2e5 + 5;
int rt[MAXN], r[MAXN], t[MAXN], d[MAXN], b[MAXN * 4], len, n, m, mx;
struct point {
    int lc, rc, sum;
    LL num;
};
point tr[MAXN * 20];
int pool = 0;
LL pre[MAXN];

inline void insert(int y, int &x, int s, int t, int p) {
    tr[x = ++pool] = tr[y];
    ++tr[x].sum; tr[x].num += (LL)b[p];
    //printf("%d %d %d %d\n", s, t, tr[x].sum, p);
    if(s == t) return;
    int mid = (s + t) >> 1;
    if(p <= mid) insert(tr[y].lc, tr[x].lc, s, mid, p);
    else insert(tr[y].rc, tr[x].rc, mid + 1, t, p);
}

inline int query(int x, int y, int s, int t, int p) {
    if(s == t) return tr[y].sum - tr[x].sum;
    int mid = (s + t) >> 1;
    int res = 0;
    if(p <= mid) res += query(tr[x].lc, tr[y].lc, s, mid, p);
    else  res += tr[tr[y].lc].sum - tr[tr[x].lc].sum + query(tr[x].rc, tr[y].rc, mid + 1, t, p);
    //printf("%d %d %d %d\n", x, y, mid, tr[y].sum);
    return res;
}

inline LL querys(int x, int y, int s, int t, int p) {
    if(s == t) return (LL)tr[y].num - tr[x].num;
    int mid = (s + t) >> 1; 
    LL res = 0;
    if(p <= mid) res += querys(tr[x].lc, tr[y].lc, s, mid, p);
    else  res += (LL)tr[tr[y].lc].num - tr[tr[x].lc].num + querys(tr[x].rc, tr[y].rc, mid + 1, t, p);
    return res;
}

inline bool check(int mid, int p, int q) {
    int now = query(0, rt[mid], 1, mx, p);
    LL nows = querys(0, rt[mid], 1, mx, p);
    LL nowp = pre[mid] - (LL)b[p] * (mid - now) - nows;
    //printf("%d %I64d %I64d\n", mid, now, nows);
    return nowp >= b[q];
}

inline void disc_init() {
    sort(b + 1, b + len + 1);
    len = unique(b + 1, b + len + 1) - b - 1;
    //for(int i = 1; i <= len; ++i) printf("%d ", b[i]); printf("\n");
    for(int i = 1; i <= m; ++i) t[i] = lower_bound(b + 1, b + len + 1, t[i]) - b;
    for(int i = 1; i <= n; ++i) {
        d[i] = lower_bound(b + 1, b + len + 1, d[i]) - b;
        r[i] = lower_bound(b + 1, b + len + 1, r[i]) - b;
    }
}

int main() {
//  freopen("1.in", "r", stdin);
//  freopen("work.out", "w", stdout);
    n = read(), m = read();
    mx = 0;
    for(int i = 1; i <= m; ++i) t[i] = read(), b[++len] = t[i], mx = max(mx, t[i]);
    for(int i = 1; i <= m; ++i)
        pre[i] = pre[i - 1] + t[i];
    for(int i = 1; i <= n; ++i) { d[i] = read(), r[i] = read(); b[++len] = d[i]; b[++len] = r[i]; }
    disc_init();
    for(int i = 1; i <= m; ++i)
        insert(rt[i - 1], rt[i], 1, mx, t[i]);
    for(int i = 1; i <= n; ++i) {
        int head = 1, tail = m, ans;
        while(head <= tail) {
            int mid = (head + tail) >> 1; 
            if(check(mid, d[i], r[i])) ans = mid, tail = mid - 1;
            else head = mid + 1;
        }
        if(check(ans, d[i], r[i])) {
            cout<<ans<<' ';
        }
        else cout<<0<<' ';
    }
} 

本题结束 :

感谢阅读本篇文章,喜欢的话,点个赞吧,你的鼓励就是我最大的动力

有什么意见,尽情发表吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值