• 2021NUAA暑假集训 Day3 题解


    比赛链接:21.7.14-NUAA暑期集训
    比赛码:NUAAACM20210714



    A - 并查集板子

    并查集模板,结果用二进制表示,注意要快读。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    int fa[4000010], ans;
    
    inline int read() {
        char ch = getchar();
        int ans = 0, f = 1;
        while (ch < '0' || ch > '9') {
            if (ch == '-') {
                f = -1;
            }
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9') {
            ans = (ans << 3) + (ans << 1) + (ch ^ 48);
            ch = getchar();
        }
        return ans * f;
    }
    
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    
    void uni(int x, int y) {
        int fax = find(x);
        int fay = find(y);
        if (fax != fay) {
            fa[fax] = fay;
        }
    }
    
    int main() {
        int n = read(), m = read();
        for (int i = 1; i <= n; ++i) {
            fa[i] = i;
        }
        for (int i = 0; i < m; ++i) {
            int act = read();
            if (act == 0) {
                int x = read(), y = read();
                uni(x, y);
            } else {
                int x = read(), y = read();
                ans = ((ans << 1) + (find(x) == find(y))) % 998244353;
            }
        }
        printf("%d", ans);
        return 0;
    }
    

    B - 线段树板子

    (cin,cout)貌似会超时,要用(scanf,printf)或者快读,考察对(lazy)标记的使用。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    #define ll long long
    
    struct Tree {
        ll num, left, right, lazy;
    } tree[4000005];
    
    ll read() {
        char x = getchar();
        ll ans = 0, f = 1;
        while (!isdigit(x)) {
            if (x == '-') f = -1;
            x = getchar();
        }
        while (isdigit(x)) {
            ans = (ans << 3) + (ans << 1) + (x ^ 48);
            x = getchar();
        }
        return ans * f;
    }
    
    void build(ll k, ll l, ll r) {
        tree[k].lazy = 0, tree[k].left = l, tree[k].right = r;
        if (l == r) {
            tree[k].num = read();
            return;
        }
        ll mid = l + r >> 1;
        build(k << 1, l, mid);
        build((k << 1) | 1, mid + 1, r);
        tree[k].num = tree[k << 1].num + tree[(k << 1) | 1].num;
    }
    
    void push_down(ll k) {
        tree[k << 1].lazy += tree[k].lazy;
        tree[(k << 1) | 1].lazy += tree[k].lazy;
        tree[k << 1].num += tree[k].lazy * (tree[k << 1].right - tree[k << 1].left + 1);
        tree[(k << 1) | 1].num += tree[k].lazy * (tree[(k << 1) | 1].right - tree[(k << 1) | 1].left + 1);
        tree[k].lazy = 0;
    }
    
    void update(ll k, ll l, ll r, ll add) {
        if (tree[k].left >= l && tree[k].right <= r) {
            tree[k].num += add * (tree[k].right - tree[k].left + 1);
            tree[k].lazy += add;
            return;
        }
        if (tree[k].lazy) push_down(k);
        ll mid = tree[k].left + tree[k].right >> 1;
        if (l <= mid) update(k << 1, l, r, add);
        if (r > mid) update((k << 1) | 1, l, r, add);
        tree[k].num = tree[k << 1].num + tree[(k << 1) | 1].num;
    }
    
    ll query(ll k, ll l, ll r) {
        if (tree[k].left >= l && tree[k].right <= r) {
            return tree[k].num;
        }
        if (tree[k].lazy) push_down(k);
        ll mid = tree[k].left + tree[k].right >> 1;
        ll ans = 0;
        if (l <= mid) ans += query(k << 1, l, r);
        if (r > mid) ans += query((k << 1) | 1, l, r);
        return ans;
    }
    
    int main() {
        ll n, m, add, x, y, act;
        n = read(), m = read();
        build(1, 1, n);
        while (m--) {
            act = read(), x = read(), y = read();
            if (act == 1) {
                add = read();
                update(1, x, y, add);
            }
            if (act == 2) {
                printf("%lld
    ", query(1, x, y));
            }
        }
        return 0;
    }
    

    树状数组版:

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    long long n, q, bit0[1000010], bit1[1000010];
    
    void updata(long long *bit, long long x, long long add) {
        while (x <= n) {
            bit[x] += add;
            x += x & -x;
        }
    }
    
    long long getSum(long long *bit, long long x) {
        long long sum = 0;
        while (x > 0) {
            sum += bit[x];
            x -= x & -x;
        }
        return sum;
    }
    
    int main() {
        scanf("%lld%lld", &n, &q);
        long long act, add, l, r;
        for (int i = 1; i <= n; ++i) {
            scanf("%lld", &add);
            updata(bit0, i, add);
        }
        for (int i = 1; i <= q; ++i) {
            scanf("%lld", &act);
            if (act == 1) {
                scanf("%lld%lld%lld", &l, &r, &add);
                updata(bit1, l, add);
                updata(bit0, l, -add * (l - 1));
                updata(bit1, r + 1, -add);
                updata(bit0, r + 1, add * r);
            } else {
                scanf("%lld%lld", &l, &r);
                long long ans = 0;
                ans += getSum(bit1, r) * r + getSum(bit0, r);
                ans -= getSum(bit1, l - 1) * (l - 1) + getSum(bit0, l - 1);
                printf("%lld
    ", ans);
            }
        }
        return 0;
    }
    

    C - 树状数组板子

    树状数组模板,注意要开(long) (long)

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    long long bit[1000010];
    int n, q, act, l, r;
    
    int lowbit(int x) { return x & (-x); }
    
    void update(int x, int k) {
        while (x <= n) {
            bit[x] += (long long)k;
            x += lowbit(x);
        }
    }
    
    long long getSum(int x) {
        long long ans = 0;
        while (x > 0) {
            ans += bit[x];
            x -= lowbit(x);
        }
        return ans;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        while (cin >> n >> q) {
            memset(bit, 0, sizeof(bit));
            for (int i = 1, x; i <= n; ++i) {
                cin >> x;
                update(i, x);
            }
            while (q--) {
                cin >> act >> l >> r;
                if (act == 2) {
                    cout << getSum(r) - getSum(l - 1) << endl;
                } else {
                    update(l, r);
                }
            }
        }
        return 0;
    }
    

    D - 单调队列板子

    用两个双向队列(deque)模拟单调队列来维护区间,一个单调递增,一个单调递减,使当前区间的最大最小值分别出现在两个队列的队首。

    #include <cstdio>
    #include <deque>
    #include <iostream>
    using namespace std;
    
    int n, k, a[1000010];
    
    int main() {
        deque<int> maxn, minn;
        scanf("%d%d", &n, &k);
        for (int i = 0; i < n; ++i) {
            scanf("%d", &a[i]);
        }
        for (int i = 0; i < k; ++i) {  // 单调队列
            while (!minn.empty() && a[i] < minn.back()) {
                minn.pop_back();
            }
            minn.push_back(a[i]);
            while (!maxn.empty() && a[i] > maxn.back()) {
                maxn.pop_back();
            }
            maxn.push_back(a[i]);
        }
        // 窗口移动求最小
        for (int i = k; i < n; ++i) {
            cout << minn.front() << " ";
            if (minn.front() == a[i - k]) {
                minn.pop_front();
            }
            while (!minn.empty() && a[i] < minn.back()) {
                minn.pop_back();
            }
            minn.push_back(a[i]);
        }
        cout << minn.front() << " ";
        cout << endl;
        // 窗口移动求最大
        for (int i = k; i < n; ++i) {
            cout << maxn.front() << " ";
            if (maxn.front() == a[i - k]) {
                maxn.pop_front();
            }
            while (!maxn.empty() && a[i] > maxn.back()) {
                maxn.pop_back();
            }
            maxn.push_back(a[i]);
        }
        cout << maxn.front() << " ";
        cout << endl;
        return 0;
    }
    

    E - 带权并查集

    正解是带权并查集,这里选用了一种开三倍并查集的思想。
    开了三倍大小的标记数组来表示三个物种,(1)(n)(A)物种,(n+1)(2 imes n)(B)物种,(2 imes n + 1)(3 imes n)(C)物种。
    如果(u)(v),则相对的(u+n)(v)为一个物种,(u+2 imes n)(v + n)为一个物种,(u)(v + 2 imes n)为一个物种。
    通过并查集关联同一物种。

    #include <iostream>
    #include <cstdio>
    #include <ctype.h>
    using namespace std;
    #define MAXN 100010
    #define INF 100000000
    #define ll long long
    
    ll read() {     // fast read
        ll ans = 0; int f = 1;  char x = getchar();
        while (!isdigit(x)) {
            if (x == '-')
                f = -1;
            x = getchar();
        }
        while (isdigit(x)) {
            ans = (ans << 3) + (ans << 1) + (x ^ 48);
            x = getchar();
        }
        return ans * f;
    }
    
    int fa[150050], n, k, act, u, v, ans;
    
    int find(int x) {   // find father
        return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    
    int main() {
        n = read(), k = read();
        for (int i = 1; i <= n; ++i) {
            fa[i] = i;
            fa[i + n] = i + n;
            fa[i + n * 2] = i + n * 2;
        }
        while (k--) {
            act = read(), u = read(), v = read();
            if (u > n || v > n) {
                ans++;
                continue;
            }
            else if (act == 1) {
                if (find(u) == find(v + n) || find(v) == find(u + n)) {
                    ans++;
                }
                else {  // same father
                    fa[find(u)] = find(v);
                    fa[find(u + n)] = find(v + n);
                    fa[find(u + n * 2)] = find(v + n * 2);
                }
            }
            else {
                if (find(u) == find(v + n) || find(u) == find(v)) {
                    ans++;
                }
                else {  // u -> v
                    fa[find(u + n)] = find(v);
                    fa[find(u + n * 2)] = find(v + n);
                    fa[find(u)] = find(v + n * 2);
                }
            }
        }
        cout << ans;
        return 0;
    }
    

    正解如下:

    // 带权并查集
    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    int f[50005], d[50005], n, k, d1, x, y, ans;
    
    int find(int x) {
        if (x != f[x]) {
            int xx = f[x];
            f[x] = find(f[x]);
            d[x] = (d[x] + d[xx]) % 3;
        }
        return f[x];
    }
    
    int main() {
        scanf("%d%d", &n, &k);
        for (int i = 1; i <= n; i++) {
            f[i] = i;
            d[i] = 0;
        }
        for (int i = 1; i <= k; i++) {
            scanf("%d%d%d", &d1, &x, &y);
            if ((d1 == 2 && x == y) || (x > n || y > n)) {
                ans++;
                continue;
            }
            if (d1 == 1) {
                if (find(x) == find(y)) {
                    if (d[x] != d[y]) ans++;
                } else {
                    d[f[x]] = (d[y] - d[x] + 3) % 3;
                    f[f[x]] = f[y];
                }
            }
            if (d1 == 2) {
                if (find(x) == find(y)) {
                    if (d[x] != (d[y] + 1) % 3) ans++;
                } else {
                    d[f[x]] = (d[y] - d[x] + 4) % 3;
                    f[f[x]] = f[y];
                }
            }
        }
        printf("%d
    ", ans);
        return 0;
    }
    

    F - ST表板子

    ST表模板。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    
    int num[50005], minn[50005][50], maxn[50005][50], n, q;
    
    void stPreWork() {
        for (int i = 1; i <= n; ++i) {
            minn[i][0] = num[i];
            maxn[i][0] = num[i];
        }
        for (int j = 1; (1 << j) <= n; ++j) {
            for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
                minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]);
                maxn[i][j] = max(maxn[i][j - 1], maxn[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    
    int stQuery(int l, int r) {
        int k = 0;
        while (l + (1 << (k + 1)) - 1 <= r) {
            k++;
        }
        return max(maxn[l][k], maxn[r - (1 << k) + 1][k]) - min(minn[l][k], minn[r - (1 << k) + 1][k]);
    }
    
    int main() {
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &num[i]);
        }
        stPreWork();
        for (int i = 1, l, r; i <= q; ++i) {
            scanf("%d%d", &l, &r);
            printf("%d
    ", stQuery(l, r));
        }
        return 0;
    }
    

    G - 要求用并查集做

    通过并查集来建立信息传递关系。

    #include <iostream>
    using namespace std;
    
    int n, fa[200010], ans = 0x3f3f3f3f, cnt, x;
    
    int get(int x) {
        cnt++;
        return fa[x] == x ? x : get(fa[x]);
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            fa[i] = i;
        }
        for (int i = 1; i <= n; ++i) {
            cnt = 0;
            cin >> x;
            if (get(x) == i) {
                ans = min(ans, cnt);
            } else {
                fa[i] = x;
            }
        }
        cout << ans;
        return 0;
    }
    

    H - 欧拉筛

    由于任意一个数(n)可以表示为(n=p^{a_1}_1 + {p}^{a_2}_2+...)(p_1,p_2...)为质数。
    所以(n=p_i^{a_i} imes x),由题意知(a_i)只能为(1)(2),否则无法拆分出符合条件的情况。
    (a_i)(1)时,一个素数有两种拆分方式,所以贡献为(2);当(a_i)(2)时,只有将一个(p_i)分配到(x)里去,且(x)本身不可整除(p_i)时,才能满足条件,因此贡献为(1)
    可得到递推式:(left{ egin{matrix} &f(n)=2 imes f(x),&a_i=1 \ &f(n)=f(x),&a_i=2 \ &f(n)=0,&a_i=3 end{matrix} ight.)
    再用前缀和记录结果。每次查询为(O(1))

    #include <cstdio>
    #include <iostream>
    using namespace std;
    #define N 20000010
    
    bool vis[N];
    int f[N], prime[N], T, n;
    long long sum[N];
    
    void init() {
        int cnt = 0;
        f[1] = 1;
        for (int i = 2; i <= N; ++i) {
            if (!vis[i]) {
                prime[++cnt] = i;
                f[i] = 2;
            }
            for (int j = 1; j <= cnt && (prime[j] * i) < N; ++j) {
                int num = i * prime[j];
                vis[num] = true;
                if (i % prime[j]) {
                    f[num] = 2 * f[i];
                } else if (i % (prime[j] * prime[j]) == 0) {
                    f[num] = 0;
                } else {
                    f[num] = f[i / prime[j]];
                    break;
                }
            }
        }
        for (int i = 1; i <= N; ++i) {
            sum[i] = sum[i - 1] + f[i];
        }
    }
    
    int main() {
        init();
        scanf("%d", &T);
        while (T--) {
            scanf("%d", &n);
            printf("%lld
    ", sum[n]);
        }
        return 0;
    }
    
  • 相关阅读:
    一分钟搞懂列式与行式数据库(转)
    docker daemon 配置文件
    Docker-删除untagged docker images
    全栈JavaScript之路(十三)了解 ElementTraversal 规范
    static, readonly, const
    Timer与AlarmManager的差别
    计算客 (人人都有极客精神)爆力
    nginx 配置web 虚拟文件夹 而且codeIgniter,thinkphp 重定向url 地址
    单例模式之 懒汉模式普通版
    POJ 3468 A Simple Problem with Integers 【树状数组】
  • 原文地址:https://www.cnblogs.com/IzumiSagiri/p/15013682.html
Copyright © 2020-2023  润新知