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大的类似。

原文地址:https://www.cnblogs.com/Inko/p/11411315.html