自嗨测试赛5

A Beautiful

题目大意 : 一个n×n的矩阵,每行是排列,要求相邻两行要错排,给出一个矩阵问按字典序从小到大排序后的排名

  • 枚举在哪一行字典序小,前面的与给的一样,后面的任意排,再枚举这一行中哪一个字典序小,前面的与给的一样,后面的任意排

  • 这样就能求出比这个矩阵字典序小的有多少个

  • 具体求的时候,第一行是个排列,康托展开模板,没听说过应该也会

  • 后面的行都得错排,枚举哪一个的时候后面的不一定都要错排,要统计一下需要错排的有几个,然后用扩展错排,打表可以找到规律,网上一篇题解上的式子是错的。

Code

Show Code
#include <cstdio>

using namespace std;
const int N = 2005, M = 19990921;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
    return x * f;
}

int n, a[N][N], f[N][N], p[N], fac[N], ans;
//f[i][j]:i个数j个限制的错排

struct Tree {
    int t[N], v[N];
    void Add(int x, int w) {
        if (w == -1 && !v[x]) return; v[x] ^= 1;
        for (; x <= n; x += x & -x) t[x] += w;
    }
    int Ask(int x, int w = 0) {
        for (; x; x -= x & -x) w += t[x];
        return w;
    }
    void Init() {
        for (int i = 1; i <= n; ++i) t[i] = i&-i, v[i] = 1;
    }
}t1, t2;

int main() {
    freopen("beautiful.in", "r", stdin);
    freopen("beautiful.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= n; ++j)
            a[i][j] = read();
    f[0][0] = 1; f[1][1] = 0; f[2][2] = 1; 
    for (int i = 3; i <= n; ++i)
        f[i][i] = 1ll * (i - 1) * (f[i-1][i-1] + f[i-2][i-2]) % M;
    for (int i = 1; i <= n; ++i)
        for (int j = i-1; j >= 0; --j)
            if ((f[i][j] = f[i][j+1] + f[i-1][j]) >= M) f[i][j] -= M;
    fac[0] = p[0] = 1;
    for (int i = 1; i <= n; ++i) {
        fac[i] = 1ll * fac[i-1] * i % M;
        p[i] = 1ll * p[i-1] * f[n][n] % M;
    }
    //for (int i = 0; i <= n; ++i, puts("")) for (int j = 0; j <= i; ++j) printf("%d ", f[i][j]);
    t1.Init();
    for (int j = 1; j <= n; ++j)
        t1.Add(a[1][j], -1), ans = (ans + 1ll * t1.Ask(a[1][j]) * fac[n-j]) % M;
    ans = 1ll * ans * p[n-1] % M;
    for (int i = 2; i <= n; ++i) {
        int sum = 0; t1.Init(); t2.Init();
        for (int j = 1; j <= n; ++j) {
            int tot, cnt, tmp; 
            //tot 这个位置能选多少个
            //tmp 有多少个限制
            //cnt 这个位置多少种选法会使限制-1
            t1.Add(a[i][j], -1); tot = t1.Ask(a[i][j]);
            if (a[i-1][j] < a[i][j] && t1.v[a[i-1][j]]) tot--;
            t2.Add(a[i-1][j], -1); tmp = t2.Ask(n);
            cnt = t2.Ask(a[i][j] - 1);
            sum = (sum + 1ll * cnt * f[n-j][tmp-1] + 1ll * (tot - cnt) * f[n-j][tmp]) % M;
            t2.Add(a[i][j], -1);
        }
        ans = (ans + 1ll * sum * p[n-i]) % M;
    }
    printf("%d\n", ans + 1);
    return 0;
}

B Exchange

题目大意 : 支持区间加,区间求和,交换区间

  • 开始想的FHQ,但是没学过,突然就想到lct维护,当时感觉特别妙,交换区间的时候Cut四次,然后Link四次,考场上调了3个多小时没调出来

  • 正解是动态开点线段树,因为交换的区间有特殊性质,初始区间设为0到2^k-1,交换的时候只需要交换两个节点,真的是秒极了

  • 交换完记得Pushup一下,不然查询的时候会出错

Code

Show Code
#include <cstdio>
#include <algorithm>
#define ls t[rt].l
#define rs t[rt].r

using namespace std;
typedef long long ll;
const int N = 2.5e7 + 5;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; (c < '0' || c > '9'); c = getchar()) if (c == '-') f = -1;
    for (;!(c < '0' || c > '9'); c = getchar()) x = x * 10 + c - '0';
    return x * f;
}

int n, m, M, opt, trc = 1;
ll ans;

struct Tree {
    int l, r, tag; ll s;
}t[N];

void Update(int rt, int l, int r, int w) {
    t[rt].s += 1ll * w * (r - l + 1); t[rt].tag += w;
}

void Pushdown(int rt, int l, int r) {
    if (!ls) ls = ++trc;
    if (!rs) rs = ++trc;
    if (!t[rt].tag) return;
    int mid = l + r >> 1; 
    Update(ls, l, mid, t[rt].tag); 
    Update(rs, mid+1, r, t[rt].tag); 
    t[rt].tag = 0;
}

void Add(int rt, int l, int r, int x, int y, int w) {
    if (x <= l && r <= y) return Update(rt, l, r, w);
    int mid = l + r >> 1; Pushdown(rt, l, r);
    if (x <= mid) Add(ls, l, mid, x, y, w);
    if (y >  mid) Add(rs, mid+1, r, x, y, w);
    t[rt].s = t[ls].s + t[rs].s;
}

int Find(int rt, int l, int r, int x, int y) {
    if (x <= l && r <= y) return rt;
    int mid = l + r >> 1; Pushdown(rt, l, r);
    if (y <= mid) return Find(ls, l, mid, x, y);
    else return Find(rs, mid + 1, r, x, y);
}

ll Ask(int rt, int l, int r, int x, int y) {
    if (x <= l && r <= y) return t[rt].s;
    int mid = l + r >> 1; ll ans = 0; Pushdown(rt, l, r);
    if (x <= mid) ans = Ask(ls, l, mid, x, y);
    if (y > mid) ans += Ask(rs, mid + 1, r, x, y);
    return ans;
}

int main() {
    freopen("exchange.in", "r", stdin);
    freopen("exchange.out", "w", stdout);
    n = read(); m = read(); opt = read(); M = n + 1;
    for (int i = 1; i <= n; i *= 2)
        if (i * 2 - 1 > n) n = i * 2 - 1;
    while (m--) {
        int od = read(), l = read(), r = read();
        if (opt) l = (l + ans) % M, r = (r + ans) % M;
        if (od == 1) {
            int x = read();
            if (opt) x = (x + ans) % M;
            Add(1, 0, n, l, r, x);
        }
        else if (od == 2) {
            int x = read();
            if (opt) x = (x + ans) % M;
            x = 1 << x; l *= x; r *= x;
            swap(t[Find(1, 0, n, l, l + x - 1)], t[Find(1, 0, n, r, r + x - 1)]);
            Add(1, 0, n, l, l + x - 1, 0);
            Add(1, 0, n, r, r + x - 1, 0); 
        }
        else printf("%lld\n", ans = Ask(1, 0, n, l, r));
    }
    return 0;
}

C Embedding Enumeration (Unaccepted)

题目大意 :

  • 巨型分类讨论dp

Code

Show Code
posted @ 2021-03-12 19:54  Shawk  阅读(68)  评论(0)    收藏  举报