返回顶部

2017-2018 ACM-ICPC Asia Tsukuba Regional Contest

Problem C - Medical Checkup

题意(大概):用n个人要排队体检,都必须从第1个项目开始体检,依次体检下去。其中第2个人必须在第1个人检查完第1个项目之后开始检查第1个项目。某个人体检每个项目的时间花费都是确定的。问t时刻每个人正在体检的项目是什么,假如t时刻某人已经体检完成,则输出他正在准备体检的项目。

先考虑一个简单的问题,从第1个人开始,那么答案就直接是:\(x_1=\lfloor\frac{t_1}{a_1}\rfloor+1\),这个应该很好解释……直接取下整(已经完全体检完成的项目),然后+1(正在进行/准备进行的项目)。

然后\(t_2=t_1-a_1\)(后面的人的时间都被第一个人的体检占去了)

第2个人就复杂一些,首先他是从第1个人把第1个项目释放之后才开始体检,假如他不比第1个人的时间长,那么他就可以一直跟着第1个人做,最后特殊判断一下他有没有体检完成。画个图就可以看懂。反之,假如他比第1个人长,那么对他来说就不需要等,直接套第1个人的算法就可以得结果。

回过头看,其实第1个人不加特判也可以这样做。

也就是:

\(maxa_i=max(maxa_{i-1},a_i)\)

\(x_i=\lfloor\frac{t_i}{maxa_i}\rfloor+1+[t_i\; mod \; maxa_i \ge a_i]\)

\(t_i=t_{i-1}-a_i\)

上面的算法有个致命漏洞就是\(t_i=t_{i-1}-max\{a_i\}\)可能会出负数,WA掉一发,修正之后是:

\(max\{a_i\}=max(maxa_i,a_i)\)

\(x_i=\lfloor\frac{t_i}{maxa_i}\rfloor+1+[t_i\; mod \; maxa_i \ge a_i]\)

\(t_i=t_{i-1}-min(t_{i-1},a_i)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n, t;
    while(~scanf("%d%d", &n, &t)) {
        int a, maxa = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a);
            maxa = max(maxa, a);
            printf("%d\n", t / maxa + 1 + (t % maxa >= a));
            t -= min(t, a);
        }
    }
}

Problem I - Starting a Scenic Railroad Service

题意:有n个人坐火车,每个人有个要坐的区间[L,R],有两种出票方法:一种是每个人先到先选,另一种是接收全部人的请求后统一安排。先到先选意思是,当他开始订票的时候,他这个区间里有空位的话,那么他可以选任意一个空位。求最小的座位安排,使得无论订票的顺序是什么,每个人都有得坐。

首先说第二种,貌似这种就是每次给[L,R]区间覆盖+1,最后取所有区间的最大值,现场时我用的一个线段树来做,实际上只需要差分最后统计一波前缀和。

然后是第一种,总是能够构造一种方法,使得最坏的情况为某个人乘车区间内车上乘客的总数,非常简单,假设少1个位,那么让其他人每个人都选一个位坐那里,然后这个长区间的就没办法坐了。而不会需要更多的位置,这个应该是很显然的。这个可以用两个数组pre和suf表示每个时间前结束的人数、每个时间后开始的人数,那么pre[L]和suf[R]就是不会和这个人有相交的人数,剩下的都是相交的。

所以这道题是可以开到1e7的。

也有人是用二分+主席树做的,感觉很沙雕……太复杂了,还是上面简单。

注意各种+1,-1的情况就可以了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

#define ls (o<<1)
#define rs (o<<1|1)

const int MAXN = 100000;
int st[(MAXN << 2) + 5], lazy[(MAXN << 2) + 5];

inline void PushUp(int o) {
    st[o] = max(st[ls], st[rs]);
}

inline void PushDown(int o) {
    if(lazy[o]) {
        st[ls] += lazy[o];
        lazy[ls] += lazy[o];
        st[rs] += lazy[o];
        lazy[rs] += lazy[o];
        lazy[o] = 0;
    }
}

inline void Update(int o, int l, int r, int ql, int qr, int v) {
    if(ql <= l && r <= qr) {
        st[o] += v;
        lazy[o] += v;
    } else {
        PushDown(o);
        int m = (l + r) >> 1;
        if(ql <= m)
            Update(ls, l, m, ql, qr, v);
        if(qr >= m + 1)
            Update(rs, m + 1, r, ql, qr, v);
        PushUp(o);
    }
}

inline int QueryMax(int o, int l, int r, int ql, int qr) {
    if(ql <= l && r <= qr) {
        return st[o];
    } else {
        PushDown(o);
        int m = (l + r) >> 1, res = 0;
        if(ql <= m)
            res = QueryMax(ls, l, m, ql, qr);
        if(qr >= m + 1)
            res = max(res, QueryMax(rs, m + 1, r, ql, qr));
        return res;
    }
}

int L[200005], R[200005];
int pre[MAXN + 5], suf[MAXN + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n;
    while(~scanf("%d", &n)) {
        int maxans = 0, minans = n;
        memset(st, 0, sizeof(st[0]) * (n + 1));
        memset(lazy, 0, sizeof(lazy[0]) * (n + 1));
        memset(pre, 0, sizeof(pre));
        memset(suf, 0, sizeof(suf));
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &L[i], &R[i]);
            pre[R[i] - 1]++;
            suf[L[i]]++;
            Update(1, 1, MAXN, L[i], R[i] - 1, 1);
        }
        for(int i = 1; i <= MAXN; ++i)
            pre[i] += pre[i - 1];
        for(int i = MAXN; i >= 1; --i)
            suf[i] += suf[i + 1];
        for(int i = 1; i <= n; ++i)
            maxans = max(maxans, n - pre[L[i] - 1] - suf[R[i]]);
        minans = min(minans, QueryMax(1, 1, MAXN, 1, MAXN));
        printf("%d %d\n", maxans, minans);
    }
}

假如用差分的话就不需要+1-1了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 200000, MAXP = 100000;
int L[MAXN + 5], R[MAXN + 5];
int pre[MAXP + 5], suf[MAXP + 5], dif[MAXP + 5];

int main() {
#ifdef Yinku
    freopen("Yinku.in", "r", stdin);
#endif // Yinku
    int n;
    while(~scanf("%d", &n)) {
        int ans1 = 0, ans2 = 0, sum = 0;
        memset(dif, 0, sizeof(dif));
        memset(pre, 0, sizeof(pre));
        memset(suf, 0, sizeof(suf));
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &L[i], &R[i]);
            ++pre[R[i]], ++suf[L[i]];
            ++dif[L[i]], --dif[R[i]];
        }
        for(int i = 1; i <= MAXP; ++i)
            pre[i] += pre[i - 1];
        for(int i = MAXP; i >= 1; --i)
            suf[i] += suf[i + 1];
        for(int i = 1; i <= n; ++i)
            ans1 = max(ans1, n - pre[L[i]] - suf[R[i]]);
        for(int i = 1; i <= MAXP; ++i) {
            sum += dif[i];
            ans2 = max(ans2, sum);
        }
        printf("%d %d\n", ans1, ans2);
    }
}

pre[x] 表示x位置及其之前就结束的, suf[x] 表示x位置及其之后才开始的。那么不相交的显然就是 n - pre[L[i]] - suf[R[i]] 。而差分就更简单了,因为是线段覆盖,所以就统一用每段的左端点来表示。

这题假如数据范围出大一点的话,就需要离散化,离散化之后也是O(n),(没有离散化是O(n+maxp))。

posted @ 2019-10-26 23:12  Inko  阅读(195)  评论(0编辑  收藏  举报