zoukankan      html  css  js  c++  java
  • Codeforces Round #507 (Div. 2, based on Olympiad of Metropolises)

    题目链接:https://codeforces.com/contest/1040

    A - Palindrome Dance

    int x[1005];
    void test_case() {
        int n, a, b;
        scanf("%d%d%d", &n, &a, &b);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &x[i]);
        int sum = 0;
        for(int i = 1; i <= n / 2; ++i) {
            if(x[i] == 0) {
                if(x[n + 1 - i] == 1) {
                    puts("-1");
                    return;
                } else if(x[n + 1 - i] == 2)
                    sum += a;
            } else if(x[i] == 1) {
                if(x[n + 1 - i] == 0) {
                    puts("-1");
                    return;
                } else if(x[n + 1 - i] == 2)
                    sum += b;
            } else {
                if(x[n + 1 - i] == 0)
                    sum += a;
                else if(x[n + 1 - i] == 1)
                    sum += b;
                else
                    sum += 2 * min(a, b);
            }
        }
        if(n % 2 == 1 && x[(n + 1) / 2] == 2)
            sum += min(a, b);
        printf("%d
    ", sum);
    }
    

    B - Shashlik Cooking

    题意:给一个长度为 (n) 的01串,初始全为0。给定长度 (k) ,每次选一个位置 (x) ,翻转包含 (x) 在内的 ([x-k,x+k]) ,翻转最少的次数使得全部为1。

    题解:单次翻转可以翻转 (2k+1) 个位置,所以最少需要 (lceilfrac{n}{2k+1} ceil) 次。设 (c=lceilfrac{n}{2k+1} ceil) ,那么最长是 (c(2k+1)) ,最短是 ((c-2)(2k+1)+2(k+1)) ,恰好覆盖,所以中间一定存在一种构造。下面是字典序最小的构造。

    void test_case() {
        int n, k;
        scanf("%d%d", &n, &k);
        int c = (n + (2 * k + 1 - 1)) / (2 * k + 1);
        int b;
        for(b = 1;; ++b) {
            if(b + k + (c - 1) * (2 * k + 1) >= n)
                break;
        }
        printf("%d
    ", c);
        for(int i = 1; i <= c; ++i)
            printf("%d%c", b + (i - 1) * (2 * k + 1), " 
    "[i == c]);
    }
    

    *C - Timetable

    这个C题特别有收获。

    题意:有一个长度为 (n(1leq n leq 200000)) 的严格单调上升的正整数序列 (a(1leq a_i leq 10^{18})) ,和一个大正整数 (t(1leq t leq 10^{18})) 。又给一个长度为 (n) 的序列 (x) ,其中 (x_i) 表示 (a_i) 最大能够匹配的是 (b_{x_i})(a_i)(b_j) 匹配是指 (a_i+tleq b_j) 。要求构造单调递增的序列 (b)

    错误算法1:首先序列 (x) 应该是非严格单调上升的,因为一个出发更晚的车可以选的范围肯定是只有右边的一段。所以先验证序列 (x) 合法。然后有个猜测,就是连续相同的一段 ([x_i,x_j]) 是可以互换的,要求他们可以互换则要 (b_igeq a_j+t) ,注意这些都要让 (a_{j+1}) 到不了(不参与互换),因为假如 (a_{j+1}) 能到的话,可以让 (a_{j+1})(b_{j})(a_{j}) 可以去 (b_{j+1}) ,与 ([x_i,x_j]) 是最长的连续矛盾。

    所以还要让 (b_j<a_{j+1}+t)

    如:

    3 10
    4 6 8
    2 2 3
    

    可恰好构造为:

    16 17 18
    

    这样一来 8 不能到 16 和 17 ,就只能去 18 ,18 被 8 占领了之后 4 和 6 当然就不能去 18 ,要保证 4 和 6 可互换所以第一个数最小要构造为 6+10=16 ,而这时候恰好可以放下一个 17 。总之就是要让 17 不能被 8 到,所以这段要尽可能小。

    所以就按尽可能小来构造,然后验证每段 ([x_i,x_j]) 是不能到达前一段的。

    但是很可惜这个算法是有问题的,首先要加一个验证就是 (x_i>=i) ,然后会在这组数据翻车:

    10 200
    100 101 103 104 106 107 109 111 113 114
    2   2   4   4   7   7   7   8   12  12
    

    输出

    Yes
    301 302 304 305 309 310 311 311 314 315
    

    错误算法2:发现问题就是让311重复了,原因是 106~109 这段是从 309 开始构造的,但是实际上可以从 308 开始构造,因为“连续相同的一段 ([x_i,x_j]) 是可以互换的”是个假命题,实际上是不需要互换的,只需要循环左移就可以让 106 取得 x=7 。所以这个连续段中除去最后一个的每个 (b_i) 只需让 (a_{i+1}) 能到就行。

    不知道为什么错。

    ll a[200005];
    int x[200005];
    ll b[200005];
    
    void test_case() {
        int n;
        ll t;
        scanf("%d%lld", &n, &t);
        for(int i = 1; i <= n; ++i)
            scanf("%lld", &a[i]);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &x[i]);
    
        for(int i = 2; i <= n; ++i) {
            if(x[i] < x[i - 1] || x[i] < i) {
                puts("No");
                return;
            }
        }
        for(int i = 1, nxt; i <= n; i = nxt) {
            for(nxt = i + 1; nxt <= n && x[nxt] == x[i]; ++nxt);
            for(int j = i; j < nxt - 1; ++j)
                b[j] = a[j + 1] + t;
            if(nxt <= n)
                b[nxt - 1] = a[nxt] + t - 1;
            else
                b[n] = b[n - 1] + 1;
        }
        for(int i = 2; i <= n; ++i) {
            if(b[i] <= b[i - 1]) {
                puts("No");
                return;
            }
        }
        puts("Yes");
        for(int i = 1; i <= n; ++i)
            printf("%lld%c", b[i], " 
    "[i == n]);
    }
    

    错误算法3:首先要验证几个命题。

    命题1:对所有的 (i) 满足 (1leq i leq n-1) ,都满足 (x_i leq x_{i+1}) 。简单来说就是序列 (x) 非降序。

    证明:反证法,若存在某些 (i) 满足 (1leq i leq n-1) ,但不满足 (x_i leq x_{i+1})

    (i) 是第一个不满足的位置,那么有 (x_{i}>x_{i+1}) 。由题目输入限制显然有 (i>1) ,由假设可知 (a_{i-1}) 能够到达 (b_{i-1})(a_{i-1}+tleq b_{i-1}) ;且 (a_{i}) 不能够到达 (b_{i})(a_{i}+t > b_{i}) ,但可以到达某个 (b_{j}<b_{i})(a_{i}+t leq b_{j}) 。所以有 (a_{i}+t leq b_{j} < b_{i})(a_{i}+t < b_{i})(a_{i}+t > b_{i}) 矛盾。

    命题2:对所有的 (i) 满足 (1leq i leq n) ,都满足 (x_i geq i)

    证明:反证法,若存在某些 (i) 满足 (1leq i leq n) ,但不满足 (x_i geq i)

    (i) 是第一个不满足的位置,那么有 (x_{i}<i) 。由题目输入限制显然有 (i>1) ,这个 (i) 要占用 ([1,i-1]) 的其中一个,那么前面就必定有一个要去到 ([i,n]) ,这样就与命题1矛盾。

    命题3:某个连续相等的 (x) 段,使得序列 (b) 尽可能小的构造是循环左移

    直观感觉?要使得第一个位置L能够换去最后一个位置R,那么R要换去[L,R-1],这时只换去R-1是最好的。这样除了最后一个位置以外,每个位置只受到 (b_igeq a_{i+1}+t) 的限制,其他的置换限制会更多。

    命题4:两个相邻的连续相等的 (x) 段,断层处必定满足 (b_{x_{1R}}<a_{x_{2L}}+t)

    证明:否则 (x_{2L}) 就可以来 (x_{1R}) 了,而 (x_{1R}) 肯定也能去 (x_{2L}) ,这样就不是断层了。

    所以最末尾的 (b_n) 几乎不受任何限制,取最大值,然后每次断层会导致 (b_i) 突然下降。同时连续段内的除了最后一个元素之外的 (b_i) 还要受到 (b_i geq a_{i+1}+t) 的限制用来循环左移,非连续段内(也就是断层)就是 (b_i geq a_i+t)(b_i < a_{i+1}+t) 。后者出现矛盾时无解。

    题解:理解错题意了,根本不是这个意思。只有两种位置:1、 (x_i=i) 的位置,即连续的段的末尾。这个时候若后面还有数,则不能从 (a_{i+1}) 转移,为了让 (b) 尽可能递增所以贪心取为 (a_{i+1}+t-1) ;否则后面没有数,可以取为 (a_{n}+t+1) 到无穷大之间的数 (一定要+1,因为倒数第二个数假如是type2的话就会相等)。2、其他位置。那么后面必定有对应长度的连续段,只需要下一班车 (i+1) 可以停过来就可以了,为了让 (b) 尽可能递增取为 (a_{i+1}+t) 。这样取完之后能够保证每个数至少能够取到 (x_i) ,但是不能保证 (b) 一定是递增的也不能保证恰好能取到 (x_i) (有可能会继续往右边延伸,因为这样构造的 (b) 有可能有位置是相等的,也有可能 (b) 是严格单调递增的,但是 (a_i) 确实被某些 (>x_i) 的位置蔓延到)

    终于想明白这道题的本质了!

    某个位置假如能够被后车占了,则这个位置是“可被后车占领位置”,自己就可以把车开到这一段“可被后车占领位置”的最后一个的下一个位置(这些“可被后车占领位置”逐个被后车占领,自己开去最后一个);假如某个 (x_i eq i) 那么这个位置就必须是连续位置,至少要有 (b_i geq a_{i+1}+t) ;这样只能保证自己是一个可以往后开的,但是能开多远是 无法保证 的。要在最后确认。

    否则,这个位置不能被后车占了,那么必须有 (b_i < a_{i+1}+t) ,即至多 (b_i leq a_{i+1}+t-1)

    由贪心法,是从左往右构造,对于每个“至少”的条件要恰好满足,而“至多”的条件取满的就不容易和“至少”的条件碰在一起。

    ll a[200005];
    int x[200005];
    ll b[200005];
    
    void test_case() {
        int n;
        ll t;
        scanf("%d%lld", &n, &t);
        for(int i = 1; i <= n; ++i)
            scanf("%lld", &a[i]);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &x[i]);
    
        for(int i = 1; i <= n; ++i) {
            if(x[i] < x[i - 1] || x[i] < i) {
                puts("No");
                return;
            }
        }
    
        b[n] = a[n] + t + 1;
        for(int i = n - 1; i >= 1; --i) {
            b[i] = a[i + 1] + t - (x[i] == i);
            assert(b[i] <= b[i + 1]);
            if(b[i] == b[i + 1]) {
                puts("No");
                return;
            }
        }
        for(int i = n, lst = n; i >= 1; --i) {
            assert(x[i] <= lst);
            if(x[i] != lst) {
                puts("No");
                return;
            }
            if(b[i - 1] - a[i] < t)
                lst = i - 1;
        }
    
        puts("Yes");
        for(int i = 1; i <= n; ++i)
            printf("%lld%c", b[i], " 
    "[i == n]);
    }
    

    D - Subway Pursuit

    题意:地铁铁轨是一个长度为 (n(1leq n leq 10^{18})) 的数轴,在上面找一辆失控的车,它每次都会出现在整数位置。每次询问可以问一个区间,jury回答车是否在区间里面,注意车每次会向左或者向右移动至多 (k(0leq k leq 10)) 个单位长度,不会越界,询问至多4500次。

    题解:先用大概60~70次“二分”就可以定位车在一个长度大约 (4k) 的区间里,然后随机抽一个数字进行询问。开始询问的条件应该是再“二分”不会使得区间有显著减少时,也就是 (frac{len}{2}+2kgeq len) ,简单起见可以直接取50。注意到每个数平均会被问40次(在问一次之后下一次一般就要进行“二分”,所以有一半的次数在“二分”),所以不成功的概率应该不高。

    char s[1005];
    bool query(ll l, ll r) {
        printf("%lld %lld
    ", l, r);
        fflush(stdout);
        scanf("%s", s);
        if(s[0] == 'Y')
            return 1;
        return 0;
    }
    
    void test_case() {
        srand(time(0));
        ll n;
        int k;
        scanf("%lld%d", &n, &k);
        ll L = 1, R = n, C = 4 * k + 4;
        while(1) {
            if(R - L + 1 >= C) {
                ll M = (L + R) / 2;
                if(query(L, M)) {
                    L = max(1ll, L - k);
                    R = min(n, M + k);
                } else {
                    L = max(1ll, M + 1 - k);
                    R = min(n, R + k);
                }
            } else {
                int rnd = rand() % (R - L + 1);
                if(query(L + rnd, L + rnd))
                    return;
                else {
                    L = max(1ll, L - k);
                    R = min(n, R + k);
                }
            }
        }
    }
    
  • 相关阅读:
    csharp: Cyotek.GhostScript.PdfConversion pdf convert image
    csharp: using Acrobat.dll pdf convert images in winform
    机器学习实战---K均值聚类算法
    机器学习实战---决策树CART回归树实现
    机器学习实战---决策树CART简介及分类树实现
    机器学习实战---线性回归(更好的使用正规方程求解)
    机器学习实战---逻辑回归梯度上升(更好的理解sigmoid函数的含义并改进)
    机器学习实战---朴素贝叶斯算法使用K折交叉验证
    机器学习实战---朴素贝叶斯算法
    机器学习实战---决策树ID3算法
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12245836.html
Copyright © 2011-2022 走看看