zoukankan      html  css  js  c++  java
  • 2017-2018 ACM-ICPC Asia Tsukuba Regional Contest

    Problem C - Medical Checkup

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

    先考虑一个简单的问题,从第1个人开始,那么答案就直接是:(x_1=lfloorfrac{t_1}{a_1} floor+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=lfloorfrac{t_i}{maxa_i} floor+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=lfloorfrac{t_i}{maxa_i} floor+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
    ", 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
    ", 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
    ", ans1, ans2);
        }
    }
    

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

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

  • 相关阅读:
    sql server拆分字符串
    Pivot 和 UnPivot 实现行列转换
    SQL快速生成连续整数
    Sql Server水平分区
    各种排序算法的C语言实现
    JWT 与 Token 介绍
    python接口自动化-token关联登录
    CUDA刷新器:CUDA编程模型
    利用MONAI加速医学影像学的深度学习研究
    构建可扩展的GPU加速应用程序(NVIDIA HPC)
  • 原文地址:https://www.cnblogs.com/Inko/p/11746222.html
Copyright © 2011-2022 走看看