[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% 的输入数据 : 1≤n,m≤1000
对 100% 的输入数据 : 1≤n,m≤200000 ; 1≤t[i]≤1000000 ; 0≤d[i]≤1000000 ; 1≤r[i]≤1000000[ 注意事项 ]
如果某人浪费的时间超过一天,不需减去负的时间。
题解 :
这道题,我在考场上打的主席树, 可是在 query 的时候搞忘打 root 了,
, 气到爆炸, 但是后面调了一下发现主席树 T 掉了, 其实我一直认为这个 T 的很玄学。
接下来讲一下用树状数组的方法。 我们很容易发现前缀是满足单调的,于是我们只需要维护前缀, 然后二分。考虑维护前缀 :
首先将 t 数组和 d 数组排序(降序), 然后对每一个 d 进行处理,我们将当前大于 d 的 t 读进树状数组, 因为 t 数组和 d 数组当前满足单调, 所以当前读进树状数组的 t, 一定对后面的 d 有贡献, 且当前小与 d 的 t一定对答案没有贡献, 于是我们只需要从头扫, 下一次接着上次的状态扫。 这样每个 t 最多扫过一次。并且求出了对答案的贡献, 对与树状数组, 我们都要求出,数值的前缀和有多少天有值,就有 now=query(x)−queryday(x)∗d 。
代码 :
我加了主席树的代码
树状数组 复杂度 : n∗log22(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();
}
主席树 复杂度 : n∗log2(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<<' ';
}
}
本题结束 :
感谢阅读本篇文章,喜欢的话,点个赞吧,你的鼓励就是我最大的动力
有什么意见,尽情发表吧。