• Codeforces


    http://codeforces.com/contest/1208

    C. Magic Grid

    一开始想着直接排,结果20的时候纵向就翻车了。但4个连续自然数一组的思路是没问题的。一个偶然的机会,发现其实相差是纵向4的几个异或起来也是0。所以就是16个连续自然数一组自由构造。

    来一个迷惑性极强的构造法:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    int tg[5][5];
    int g[1005][1005];
    
    void Init() {
        int cnt = 0;
        for(int i = 1; i <= 4; ++i) {
            for(int j = 1; j <= 4; ++j) {
                tg[i][j] = cnt++;
            }
        }
    }
    
    void Random() {
        int r = rand() % 4 + 1;
        for(int i = 1; i <= 4; ++i) {
            swap(tg[i][1], tg[i][r]);
        }
        r = rand() % 4 + 1;
        for(int i = 1; i <= 4; ++i) {
            swap(tg[i][2], tg[i][r]);
        }
        r = rand() % 4 + 1;
        for(int j = 1; j <= 4; ++j) {
            swap(tg[1][j], tg[r][j]);
        }
        r = rand() % 4 + 1;
        for(int j = 1; j <= 4; ++j) {
            swap(tg[2][j], tg[r][j]);
        }
    }
    
    int Draw(int x, int y, int b) {
        Random();
        for(int i = 1; i <= 4; ++i) {
            for(int j = 1; j <= 4; ++j) {
                g[x + i - 1][y + j - 1] = tg[i][j] + 16 * b;
            }
        }
    }
    
    int bs[1000 * 1000 / 16 + 5];
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
    #endif // Yinku
        int n;
        Init();
        while(~scanf("%d", &n)) {
            int c = n * n / 16;
            for(int i = 1; i <= c; ++i) {
                bs[i] = i - 1;
            }
            random_shuffle(bs + 1, bs + 1 + c);
            int cnt = 1;
            for(int i = 1; i <= n / 4; ++i) {
                for(int j = 1; j <= n / 4; ++j) {
                    Draw(4 * (i - 1) + 1, 4 * (j - 1) + 1, bs[cnt++]);
                }
            }
            for(int i = 1; i <= n; ++i) {
                for(int j = 1; j <= n; ++j) {
                    printf("%d%c", g[i][j], " 
    "[j == n]);
                }
            }
        }
    }
    

    效果如下图,太有趣liao。

    把两个random去掉之后就蛮正常的。

    D. Restore Permutation

    昨天晚上读了假题,题目不是那个意思。原本以为i是前i个数的顺序对的个数和。那么作差就得到第i个数贡献的顺序对的个数。从右往左通过平衡树一顿操作(顺序对即排名)就可以完成构造。但是这个题并不是这个意思,他的s[i]是i之前的比i小的数字的和。qnickx太强了。搞了个线段树二分。首先确定1的位置,1比任何数都要小,放在哪里都是0,假如有多个0,那一定是最右边的0,因为假如放了左边就一定会破坏掉构造。把1放进去之后考虑把2放进去,这个时候2也是最小的,去除1的影响之后2的值也是0。所以就是需要一个带区间修改的线段树消除某个数造成的影响。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define lt ls, l, m
    #define rt rs, m + 1, r
    #define ls (o<<1)
    #define rs (o<<1|1)
    
    const ll INF = 1e18;
    const int MAXM = 200000 + 5;
    ll a[MAXM];
    ll st[MAXM * 4], lazy[MAXM * 4];
    
    inline void PushUp(int o) {
        st[o] = min(st[ls], st[rs]);
    }
    
    inline void PushDown(int o, int l, int r) {
        if(lazy[o]) {
            lazy[ls] += lazy[o];
            lazy[rs] += lazy[o];
            st[ls] += lazy[o] ;
            st[rs] += lazy[o];
            lazy[o] = 0;
        }
    }
    
    void Build(int o, int l, int r) {
        if(l == r) {
            st[o] = a[l];
        } else {
            int m = l + r >> 1;
            Build(lt);
            Build(rt);
            PushUp(o);
        }
        lazy[o] = 0;
    }
    
    void Update(int o, int l, int r, int ql, int qr, ll v) {
        if(ql <= l && r <= qr) {
            lazy[o] += v;
            st[o] += v ;
            return;
        } else {
            PushDown(o, l, r);
            int m = l + r >> 1;
            if(ql <= m)
                Update(lt, ql, qr, v);
            if(qr >= m + 1)
                Update(rt, ql, qr, v);
            PushUp(o);
        }
    }
    
    int Query(int o, int l, int r, int ql, int qr) {
        if(l == r)
            return l;
        PushDown(o, l, r);
        int m = l + r >> 1;
        if(st[rs] == 0)
            return Query(rt, ql, qr);
        else
            return Query(lt, ql, qr);
    }
    
    int Query2(int o, int l, int r, int x) {
        if(l == r)
            return st[o];
        PushDown(o, l, r);
        int m = l + r >> 1;
        if(x >= m + 1)
            return Query2(rt, x);
        else
            return Query2(lt, x);
    }
    
    
    int ans[MAXM];
    
    int main() {
    #ifdef Yinku
        freopen("Yinku.in", "r", stdin);
    #endif // Yinku
        int n;
        while(~scanf("%d", &n)) {
            for(int i = 1; i <= n; ++i)
                scanf("%lld", &a[i]);
            Build(1, 1, n);
            for(int i = 1; i <= n; ++i) {
                int p = Query(1, 1, n, 1, n);
                //消除这个元素对其他元素的影响
                Update(1, 1, n, p, n, -i);
                //防止下次再找到这个元素
                Update(1, 1, n, p, p, INF);
                ans[p] = i;
            }
            for(int i = 1; i <= n; ++i)
                printf("%d%c", ans[i], " 
    "[i == n]);
        }
    }
    

    从这道题里面突然明白在线段树上二分的原理,假如可以通过区间的信息确定要找的位置/元素在左子区间/右子区间,就可以在线段树上面二分。和主席树里面找第k大的类似。

  • 相关阅读:
    C# WinForm 只允许运行一个实例
    C# WinForm 获得文字的像素宽度
    Windows 下使用命令行升级 Npm 和 NodeJS
    每日踩坑 2020-04-15 C# 与 Java 对应 DES 加密算法
    robot自动化测试(二)--- template使用
    robot自动化测试(一)---安装
    linux io优化
    python 远程统计文件
    编程类学习资料整合,更多干货
    两份安卓学习资料,我建议你看完
  • 原文地址:https://www.cnblogs.com/Inko/p/11411315.html
Copyright © 2020-2023  润新知