2018-2019 ACM-ICPC, Asia Shenyang Regional Contest 部分题解

好吧,其实只补了E题,先写E题解吧,训练时脑袋不清醒读错题,导致没能切掉E,思路其实很好想,码量也不大
update:
C. Insertion Sort

题意:问有多少个排列, 排列的前k个元素排序后,整个排列的最长上升子序列长度为n-1
解法:第一步:设 d [ n ] d[n] 为长度为 n n 的排列中,一共有 d [ n ] d[n] 种排列 l i s lis 长度恰好为 n 1 n-1 ,很显然,我们可以在所有长度为 n 1 n-1 l i s lis 长度为 n 2 n-2 的排列最后位置插入一个 n n ,那么 d [ n ] + = d [ n 1 ] d[n]+=d[n-1] ,此外,当第倒数第二个位置上为 n 1 n-1 时,最后一个位置可以选 n 2 n-2 个数,且前 n 2 n-2 个数必须单调递增,那我门可以在倒数第二个位置后面插入 n n ,那么 d [ n ] + = n 2 d[n]+=n-2 ,对于排列: [ 1 , 2 , 3 , . . . , n 1 ] [1,2,3,...,n-1] ,除了最后一个位置,我们都可以插入 n n d [ n ] + = n 1 d[n]+=n-1
第二步:当前 k k 个数最大值为 k k 时,不难发现合法排列数: k ! ( d [ n k ] + 1 ) k!*(d[n-k]+1) ,当前 k k 个数最大值为 k + 1 k+1 时,我们枚举前 k 1 k-1 个数不存在哪个数,然后不存在的这个数可以放置后 n k n-k 个位置,那么排列数: k ! k ( n k ) k!*k*(n-k) ,当前 k k 个数最大值大于 k + 1 k+1 时,后面 n k n-k 个数必须递增的放置,合法排列数都是 k ! k!
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 55;
ll p[maxn], inv[maxn], mod;
int d[maxn];
void add(int &x, int y) {
    x += y;
    if (x >= mod)
        x -= mod;
    if (x < 0)
        x += mod;
}
ll ksm(ll x, int y) {
    ll res = 1;
    while (y) {
        if (y & 1)
            res = res * x % mod;
        x = x * x % mod;
        y /= 2;
    }
    return res;
}
void init(int n) {
    p[0] = inv[0] = 1;
    for (int i = 1; i <= n; i++) {
        p[i] = 1ll * i * p[i - 1] % mod;
        inv[i] = ksm(p[i], mod - 2);
    }
}
ll gao(int n) {
    if (n == 2)
        return 1;
    d[n] = gao(n - 1);
    add(d[n], n - 1);
    add(d[n], n - 2);
    return d[n];
}
int main() {
    int T, Case = 0;
    scanf("%d", &T);
    while (T--) {
        int n, k;
        scanf("%d%d%d", &n, &k, &mod);
        init(n);
        printf("Case #%d: ", ++Case);
        if (k >= n - 1) {
            printf("%d\n", p[n]);
            continue;
        }
        memset(d, 0, sizeof(d));
        ll tmp = gao(n - k) + 1;
        ll ans = p[k] * tmp % mod + p[k] * k % mod * (n - k) % mod + p[k] * (n - (k + 1)) % mod;
        printf("%lld\n", ans % mod);
    }
}

E. The Kouga Ninja Scrolls

题意:有n个人,每个人有坐标和宗族,有三种操作,分别是修改某个人坐标,修改某个人宗族,查询第 l 个人到第 r 个人中属于不同宗族的两个人的最大曼哈顿距离
解法:线段树没法维护曼哈顿距离,但是我们知道曼哈顿距离可以通过简单变换成切比雪夫距离,我们变换后只要维护最大最小的x坐标,最大最小的y坐标,每次查询查mx - mn即可,但是有问题,要求不同宗族,因此我们可以维护个次大x坐标,要求次大的坐标必须所属的宗族与最大所属宗族不同,次小同理,这样的话,假设mx和mn属于同一宗族,我们还可以通过次大次小来求答案
#include<bits/stdc++.h>
#define ll long long
#define pi pair<ll, int>
#define mk make_pair
#define fi first
#define se second
#define ls o * 2
#define rs o * 2 + 1
#define mid (l + r) / 2
using namespace std;
const int maxn = 1e5 + 10;
ll inf = 1e18;
struct seg {
    pi mx, mn, mx2, mn2;
    seg operator+(const seg& t) const {
        seg tmp;
        tmp.mx2 = max(mx2, t.mx2);
        tmp.mx = max(mx, t.mx);
        if (t.mx.se != tmp.mx.se && t.mx > tmp.mx2)
            tmp.mx2 = t.mx;
        if (mx.se != tmp.mx.se && mx > tmp.mx2)
            tmp.mx2 = mx;
        tmp.mn = min(mn, t.mn);
        tmp.mn2 = min(mn2, t.mn2);
        if (t.mn.se != tmp.mn.se && t.mn < tmp.mn2)
            tmp.mn2 = t.mn;
        if (mn.se != tmp.mn.se && mn < tmp.mn2)
            tmp.mn2 = mn;
        return tmp;
    }
} cat[maxn * 4], peach[maxn * 4];
ll x[maxn], y[maxn];
int c[maxn], n;
void up(int o, int l, int r, int k) {
    if (l == r) {
        cat[o].mx = cat[o].mn = mk(x[l], c[l]);
        peach[o].mx = peach[o].mn = mk(y[l], c[l]);
        cat[o].mx2 = peach[o].mx2 = mk(-inf, 0);
        cat[o].mn2 = peach[o].mn2 = mk(inf, 0);
        return;
    }
    if (k <= mid)
        up(ls, l, mid, k);
    else
        up(rs, mid + 1, r, k);
    cat[o] = cat[ls] + cat[rs];
    peach[o] = peach[ls] + peach[rs];
}
seg qu(int o, int l, int r, int ql, int qr, int opt) {
    if (l >= ql && r <= qr) {
        if (opt == 1)
            return cat[o];
        return peach[o];
    }
    if (qr <= mid)
        return qu(ls, l, mid, ql, qr, opt);
    else if (ql > mid)
        return qu(rs, mid + 1, r, ql, qr, opt);
    return qu(ls, l, mid, ql, qr, opt) + qu(rs, mid + 1, r, ql, qr, opt);
}
ll gao(int l, int r, int opt) {
    seg tmp = qu(1, 1, n, l, r, opt);
    if (tmp.mx.se != tmp.mn.se)
        return max(0ll, tmp.mx.fi - tmp.mn.fi);
    return max(0ll, max(tmp.mx.fi - tmp.mn2.fi, tmp.mx2.fi - tmp.mn.fi));
}
int main() {
    int T, Case = 0;
    scanf("%d", &T);
    while (T--) {
        printf("Case #%d:\n", ++Case);
        int m, opt, tx, ty, k;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {
            scanf("%lld%lld%d", &tx, &ty, &c[i]);
            x[i] = tx + ty;
            y[i] = tx - ty;
            up(1, 1, n, i);
        }
        while (m--) {
            scanf("%d", &opt);
            if (opt == 1) {
                scanf("%d%d%d", &k, &tx, &ty);
                x[k] += tx + ty;
                y[k] += tx - ty;
                up(1, 1, n, k);
            }
            else {
                scanf("%d%d", &tx, &ty);
                if (opt == 2) {
                    c[tx] = ty;
                    up(1, 1, n, tx);
                }
                else
                    printf("%lld\n", max(gao(tx, ty, 1), gao(tx, ty, 2)));
            }
        }
    }
}
发布了302 篇原创文章 · 获赞 98 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/ccsu_cat/article/details/101130169