其他分享
首页 > 其他分享> > AtCoder Beginner Contest 256(补题)

AtCoder Beginner Contest 256(补题)

作者:互联网

E - Takahashi's Anguish

题意:

现有\(N\)个人,现在需要你决定你个排列,第\(i\)个人讨厌第\(X_i\)个人,所以若第\(X_i\)个人排在第\(i\)个人前面的话,则会产生\(C_i\)的厌倦值,现在让你输出最小的厌倦值总和

思路:

\(n\)个人,每个人\(i\)都向\(X_i\)连一条有向边,\(n\)个人\(n\)条边,所以若产生\(k\)个连通块,就会产生\(k\)个环(一个连通块内有一个环),对于每个环,只需要选出某个放在前面,就会破坏环的结构,所以只需要挑出一个点,它的\(C_i\)值最小,累加即可。

View Code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5 + 10;
const ll inf = 1e18;
int n;
int x[N];
ll c[N];
int fa[N];
int find(int x) {
    if (fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
}
void Union(int a, int b) {
    a = find(fa[a]), b = find(fa[b]);
    if (a != b) {
        fa[a] = b;
    }
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> x[i];
        fa[i] = i;
    }
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
    }
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        int a = i, b = x[i];
        int Fa = find(fa[a]), Fb = find(fa[b]);
        if (Fa != Fb) {
            Union(Fa, Fb);
        } else {
            int now = i;
            ll res = c[i];
            now = x[now];
            res = min(res, c[now]);
            while (now != i) {
                now = x[now];
                res = min(res, c[now]);
            }
            ans += res;
        }
    }
    cout << ans << endl;
}

F - Cumulative Cumulative Cumulative Sum

题意:

长度为\(N\)的序列\(A\),\(B\)序列为\(A\)前缀和,\(C\)序列为\(B\)的前缀和,\(D\)序列为\(C\)的前缀和
现在有两种操作,\(op1\),将\(A_x\)改为\(v\),\(op2\)求\(D_x\)的值。

思路:

设\(x≥i\)
\(A_i\)对于\(B_x\)的贡献为\(1\)
\(A_i\)对于\(C_x\)的贡献为\(x-i+1\)
\(A_i\)对于\(D_x\)的贡献为\(\frac{(x-i+1)(x-i+2)}{2}\)
所以\(D_x=\frac{1}{2}\sum_{1}^{x}i^2A_i-\frac{2x+3}{2}\sum_{1}^{x}iA_i+\frac{(x-i+1)(x-i+2)}{2}\sum_{1}^{x}A_i\)
用三个树状数组分别维护\(A_i,i^2A_i,iA_i\)

View Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
#define int long long
const int mod = 998244353;
typedef pair<int, int> PII;
#define x first
#define y second
int n, T;
int a[N];
int lowbit(int x) { return x & (-x); }
int qmi(int a, int k, int mod) {
    int res = 1;
    while (k) {
        if (k & 1) res = res * a % mod;
        k >>= 1;
        a = a * a % mod;
    }
    return res;
}
struct BIT {
    int tr[N];
    BIT() { memset(tr, 0, sizeof tr); }
    void add(int x, int c) {
        for (int i = x; i < N; i += lowbit(i)) {
            tr[i] = ((tr[i] + c) % mod + mod) % mod;
        }
    }
    int query(int x) {
        int res = 0;
        for (int i = x; i > 0; i -= lowbit(i)) {
            res = ((res + tr[i]) % mod + mod) % mod;
        }
        return res;
    }
    int query(int l, int r) {
        return ((query(r) - query(l - 1)) % mod + mod) % mod;
    }
} A, B, C;
signed main() {
    cin >> n >> T;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int inv = qmi(2, mod - 2, mod) % mod;
    for (int i = 1; i <= n; i++) {
        A.add(i, i * i % mod * a[i]);
        B.add(i, a[i] * i % mod);
        C.add(i, a[i]);
    }

    int op, x, v;
    while (T--) {
        cin >> op;
        if (op == 1) {
            cin >> x >> v;

            A.add(x, (x * x % mod * (v - a[x]) % mod + mod) % mod);
            B.add(x, x * (v - a[x] + mod) % mod);
            C.add(x, (v - a[x] + mod) % mod);
            a[x] = v;
        } else {
            cin >> x;

            int res = A.query(x) * inv % mod - (2 * x + 3) % mod * inv % mod * B.query(x) +  (x + 1) * (x + 2) % mod * inv % mod * C.query(x);
            res = (res % mod + mod) % mod;
            cout << res << endl;
        }
    }
}

G - Black and White Stones

题意:

有一个正\(N\)边形,边长为\(D\)(每条边上有\(D+1\)个点),现在需要图案黑白染色,要求每条边的白点个数都相同,问有多少中染色方案?

思路:

用白色代表\(0\),黑色代表\(1\)
先单独考虑一条边的染色方案,若当前边上有\(k\)个白点,此时枚举当前这条边首尾的颜色状态分别为\(00\),\(01\),\(10\),\(11\),那么此时对应的方案数分别为\(C_{d-1}^{k-2} C_{d-1}^{k-1} C_{d-1}^{k-1} C_{d-1}^{k-2}\),由于临边的点和当前边的颜色是一样的,所以不同的边之间并不是相互独立的,所以不能直接乘起来算,由于边之间是有联系的,临边的点颜色相同,所以可以考虑通过临点把边合并起来。
定义\(f(0,0)\) \(f(0,1)\) \(f(1,0)\) \(f(1,1)\)为一条边的顶点颜色为\(00\) \(01\) \(10\) \(11\)的方案数
\(f_{2}(0,0)\)边长为\(2\)的边方案数,\(f_{2}(0,0)=f_{1}(0,1)×f_{1}(1,0)+f_{1}(0,0)×f_{1}(0,0)\)
可以发现\(\begin{bmatrix} f_{n-1}(0,0) &f_{n-1}(0,1)\\f_{n-1}(1,0)&f_{n-1}(1,1)\end{bmatrix}×\begin{bmatrix} f_{n-1}(0,0) &f_{n-1}(0,1)\\f_{n-1}(1,0)&f_{n-1}(1,1)\end{bmatrix}=\begin{bmatrix} f_{n}(0,0) &f_{n}(0,1)\\f_{n}(1,0)&f_{n}(1,1)\end{bmatrix}\)
这个可以用矩阵快速幂快速算出,最后,答案就是矩阵中\(f(0,0)\)和\(f(1,1)\)值,因为通过不断地合并边,最后首点和尾点的颜色是一样的,因为第一条边的首点就是最后一条边的尾点(可以理解成把一个点拆成两个点)。

View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
const int N = 2e4 + 10;
int n, d;
int fact[N], infact[N];

struct Matrix {
    int a[2][2];
    Matrix() { memset(a, 0, sizeof a); }
    Matrix operator*(const Matrix &t) const {
        Matrix res;
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                for (int k = 0; k < 2; k++) {
                    res.a[i][j] =
                        (res.a[i][j] + a[i][k] * t.a[k][j] % mod) % mod;
                }
            }
        }
        return res;
    }
};
int qmi(int a, int k) {
    int res = 1;
    while (k) {
        if (k & 1) res = res * a % mod;
        k >>= 1;
        a = a * a % mod;
    }
    return res;
}
void init() {
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i++) {
        fact[i] = fact[i - 1] * i % mod;
    }
    infact[N - 1] = qmi(fact[N - 1], mod - 2);
    for (int i = N - 2; i >= 1; i--) {
        infact[i] = infact[i + 1] * (i + 1) % mod;
    }
}
int C(int n, int m) {
    if (m > n || n < 0 || m < 0) return 0;
    return fact[n] * infact[n - m] % mod * infact[m] % mod;
}
Matrix Matpower(Matrix A, int k) {
    Matrix res;
    res.a[0][0] = res.a[1][1] = 1;
    while (k) {
        if (k & 1) res = res * A;
        k >>= 1;
        A = A * A;
    }
    return res;
}

signed main() {
    cin >> n >> d;
    init();

    Matrix f;
    int ans = 0;
    for (int i = 0; i <= d + 1; i++) {
        f.a[0][0] = C(d - 1, i - 2), f.a[0][1] = C(d - 1, i - 1);
        f.a[1][0] = C(d - 1, i - 1), f.a[1][1] = C(d - 1, i);

        Matrix res = Matpower(f, n);
        ans = (ans + res.a[0][0]) % mod;
        ans = (ans + res.a[1][1]) % mod;
    }
    cout << ans << endl;
}

Ex - I like Query Problem

题意:

三种操作
\(1.\)区间除法
\(2.\)区间赋值
\(3.\)区间求和,并输出

思路:

区间赋值,就是正常的区间修改操作,区间除法,由于存在区间做除法和对每个数做除法再做加法的结果不一样,所以不能直接做区间除法。所以需要是是势能线段树,需要做除法的地方就是区间的最大值等于区间的最小值,这样说明区间内的所有值都相等,此时直接转换成区间赋值操作,赋的值为最大值除以\(d\)。如果区间的值不相等,就继续递归。这里需要注意的一点就是线段树内懒标记初始化一开始不能赋值为\(0\),需要是负数,因为除法运算时,是存在除法向下取整后为\(0\)的情况,所以存在懒标记为\(0\)的情况。根据势能线段树的理论,每次操作1都是除以一个大于等于2的数,所以每个数最多操作18次就变为0了。记录一个区间最大值和区间最小值来判断当前区间内的数是否都相等。

View Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
const int N = 5e5 + 10;
const ll inf = 0x3f3f3f3f3f3f3f;
int n, m;
struct node {
    int l, r;
    ll maxv, minv;
    int add;
    ll sum;
} tr[N * 4];
ll a[N];

void pushup(int u) {
    tr[u].minv = min(tr[u << 1].minv, tr[u << 1 | 1].minv);
    tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void add(int u, int val) {
    tr[u].add = val;
    tr[u].sum = val * (tr[u].r - tr[u].l + 1);
    tr[u].minv = val;
    tr[u].maxv = val;
}
void pushdown(int u) {
    if (tr[u].add != -1) {
        add(u << 1, tr[u].add);
        add(u << 1 | 1, tr[u].add);
        tr[u].add = -1;
    }
}

void modify_div(int u, int l, int r, int d) {
    if (tr[u].l >= l && tr[u].r <= r) {
        ll mx = tr[u].maxv;
        ll mi = tr[u].minv;
        if (mx == mi) {
            add(u, tr[u].maxv / d);
            return;
        }
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) modify_div(u << 1, l, r, d);
    if (r > mid) modify_div(u << 1 | 1, l, r, d);
    pushup(u);
}

void modify_add(int u, int l, int r, int d) {
    if (tr[u].l >= l && tr[u].r <= r) {
        add(u, d);
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) modify_add(u << 1, l, r, d);
    if (r > mid) modify_add(u << 1 | 1, l, r, d);
    pushup(u);
}

ll query_sum(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) {
        return tr[u].sum;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    ll res = 0;
    if (l <= mid) res += query_sum(u << 1, l, r);
    if (r > mid) res += query_sum(u << 1 | 1, l, r);
    return res;
}
void build(int u, int l, int r) {
    if (l == r) {
        tr[u] = {l, r, a[l], a[l], -1, a[l]};
        return;
    }
    tr[u] = {l, r};
    tr[u].add = -1;
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}
signed main() {
    scanf("%lld%lld", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%lld", &a[i]);
    }
    build(1, 1, n);
    while (m--) {
        int op, l, r, d;
        scanf("%lld%lld%lld", &op, &l, &r);
        if (op == 2) {
            scanf("%lld", &d);
            modify_add(1, l, r, d);
        }
        if (op == 1) {
            scanf("%lld", &d);
            modify_div(1, l, r, d);
        }
        if (op == 3) {
            printf("%lld\n", query_sum(1, l, r));
        }
    }
    return 0;
}

标签:AtCoder,const,int,res,ll,tr,补题,256,mod
来源: https://www.cnblogs.com/OverThink/p/16398683.html