2020牛客暑期多校训练营(第八场)

Contest Info


传送门

Solved A B C D E F G H I J K
5 / 11 O - - - O - O - O - O
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. All-Star Game

对时间分治,线段树维护可撤销并查集的模板题。
这样可以直接维护每一个时刻的连通性,我们只需要求出当前连通块的个数、双方孤立的点数即可得出答案。
所以在增、添边时维护一下点的度数和孤立点数即可。

Code
// Author : heyuhhh
// Created Time : 2020/08/04 09:12:36
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]
";}
template<typename T, typename V>
  void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
  void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
  void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 4e5 + 5;

int n, m, q;
int num, cnt, blosz;
int d[N];

struct UFS {
    int f[N], h[N], sz[N], top;
    struct node {
        int x, y, fx, h, SZ, X, Y;
    }sta[N];
    void init() {
        top = 0;
        for (int i = 1; i <= n + m; i++) {
            sz[i] = (i > m ? 1 : 0);
            f[i] = i;
            h[i] = 0;
        }
    }
    int find(int x) {
        return f[x] == x ? f[x] : find(f[x]);
    }
    bool merge(int u, int v) {
        int x = find(u), y = find(v);
        if (x == y) return false;
        if (h[x] > h[y]) swap(x, y);
        
        if (++d[u] == 1) --cnt;
        if (++d[v] == 1) --num;
        if (sz[x] && sz[y]) --blosz;

        sta[++top] = node{x, y, f[x], h[y], sz[y], u, v};
        if (h[x] == h[y]) ++h[y];
        sz[y] += sz[x];
        f[x] = y;
        return true;
    }
    void undo(int k) {
        while (k--) {
            node it = sta[top--];
            f[it.x] = it.fx;
            h[it.y] = it.h;
            sz[it.y] = it.SZ;
            int u = it.X, v = it.Y;
            if (--d[u] == 0) ++cnt;
            if (--d[v] == 0) ++num;
            if (sz[it.y] && sz[it.x]) ++blosz;
        }
    }
    int query(int x) {
        int fx = find(x);
        return sz[fx];
    }
} ufs; 

vector<pii> nodes[N << 2];
int ans[N];

void add(int o, int l, int r, int L, int R, pii v) {
    if (L <= l && r <= R) {
        nodes[o].push_back(v);
        return;
    }
    int mid = (l + r) >> 1;
    if (L <= mid) {
        add(o << 1, l, mid, L, R, v);
    }
    if (R > mid) {
        add(o << 1|1, mid + 1, r, L, R, v);
    }
}

void dfs(int o, int l, int r) {
    int cc = 0;
    for (auto& it : nodes[o]) {
        int fans = it.fi, player = it.se;
        if (ufs.merge(fans, player + m)) ++cc;
    }
    if (l == r) {
        dbg(l, num, blosz);
        if (cnt > 0) ans[l] = -1;
        else ans[l] = blosz - num;
        ufs.undo(cc);
        return;
    }
    int mid = (l + r) >> 1;
    dfs(o << 1, l, mid);
    dfs(o << 1|1, mid + 1, r);
    ufs.undo(cc);
}

void run() {
    cin >> n >> m >> q;
    ufs.init();
    map<pii, int> S;
    for (int i = 1; i <= n; i++) {
        int k;
        cin >> k;
        for (int j = 1; j <= k; j++) {
            int x;
            cin >> x;
            S[MP(x, i)] = 0;
        }
    }
    for (int _ = 1; _ <= q; _++) {
        int x, y;
        cin >> x >> y;
        if (S.count(MP(x, y))) {
            add(1, 0, q, S[MP(x, y)], _ - 1, MP(x, y));
            S.erase(MP(x, y));
        } else {
            S[MP(x, y)] = _;
        }
    }
    for (auto& it : S) {
        add(1, 0, q, it.se, q, it.fi);
    }
    num = n;
    cnt = m;
    blosz = n;
    dfs(1, 0, q);
    for (int i = 1; i <= q; i++) {
        cout << ans[i] << '
';
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

E. Enigmatic Partition

假设我们知道了首项(x)以及项数(m),那么我们会发现会对在区间([(m-2)x+2x+3,(m-2)(x+2)+2x+1])([mx+3,mx+2m-3])的答案尝试贡献。
然后观察有两点:

  • 区间长度为奇数;
  • 对每个点的答案贡献不一样。

接下来就处理第二点。
稍微手模一下就会发现我们可以对区间按照长度对(4)取余进行分类,两类的贡献会有不同。
对于模(4)为1的区间长度,比如(1,5,9...),他们的贡献类似于(1,1,2,2,cdots,2,2,1,1)这样对称的,中间会有个最大值;对于另外一类区间贡献为(1,1,2,2,cdots,t,t,t,cdots,2,2,1,1),中间会有三个连续的最大值。
所以根据这个差分一下就行。
代码中我用的线段树维护奇、偶位置的差分,复杂度是两个log的(枚举(x,m)的复杂度是一个log,是类似于调和级数的,因为是(mcdot x))。
代码如下:

Code
// Author : heyuhhh
// Created Time : 2020/08/03 13:00:26
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
 
const int n = 100001;
 
ll ans[N];
 
struct segment_tree {
    ll flag[N << 2];
    void tag(int o, int v) {
        flag[o] += v;
    }
    void update(int o, int l, int r, int L, int R, int v) {
        R = min(R, n);
        if (L > R) return;
        if(L <= l && r <= R) {
            tag(o, v);
            return;
        }  
        int mid = (l + r) >> 1;
        if(L <= mid) update(o << 1, l, mid, L, R, v);
        if(R > mid) update(o << 1|1, mid + 1, r, L, R, v);
    }
    ll res;
    void query(int o, int l, int r, int p) {
        r = min(r, n);
        if (l > r) return;
        res += flag[o];
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (p <= mid) query(o << 1, l, mid, p);
        else query(o << 1|1, mid + 1, r, p);
    }
    ll query(int p) {
        res = 0;
        query(1, 1, n, p);
        return res;
    }
}A, B;
 
 
void run() {
    for (int x = 1; x <= n; ++x) {
        for (int m = 3; m <= n; ++m) {
            ll L = 1ll * m * x + 3, R = 1ll * m * (x + 2) - 3;
            if (L > (ll)100000) break;
 
            int mid = (L + R) >> 1;
            if ((R - L + 1) % 4 == 3) {
                if (L & 1) {
                    B.update(1, 1, n, L, mid, 1);
                    A.update(1, 1, n, mid + 2, R + 1, -1);
                } else {
                    A.update(1, 1, n, L, mid, 1);
                    B.update(1, 1, n, mid + 2, R + 1, -1);
                }
            } else {
                if (L & 1) {
                    B.update(1, 1, n, L, mid, 1);
                    A.update(1, 1, n, mid + 1, R + 1, -1);
                } else {
                    A.update(1, 1, n, L, mid, 1);
                    B.update(1, 1, n, mid + 1, R + 1, -1);
                }
            }
             
        }
    }
    for (int i = 1; i < n; i++) {
        ans[i] = (i & 1 ? B.query(i) : A.query(i));
    }
    for (int i = 1; i < n; i++) {
        ans[i] += ans[i - 1];
    }
    for (int i = 1; i < n; i++) {
        ans[i] += ans[i - 1];
    }
    int T;
    cin >> T;
    int _ = 0;
    while (T--) {
        ++_;
        int l, r;
        cin >> l >> r;
        cout << "Case #" << _ << ": ";
        cout << ans[r] - ans[l - 1] << '
';
    }   
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

G. Game SET

直接暴力冲就行。

I. Interesting Computer Game

建图后对每个连通块判断是否存在环即可。

Code
// Author : heyuhhh
// Created Time : 2020/08/03 12:14:49
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
int _;
void run() {
    ++_;
    int n;
    cin >> n;
    map<int, int> mp;
    vector<vector<int>> G(2 * n);
    int tot = 0;
    vector<int> d(2 * n);
    for (int i = 1; i <= n; i++) {
        int u, v;
        cin >> u >> v;
        if (mp.find(u) == mp.end()) {
            mp[u] = tot++;
        }
        if (mp.find(v) == mp.end()) {
            mp[v] = tot++;
        }
        G[mp[u]].push_back(mp[v]);
        G[mp[v]].push_back(mp[u]);
        ++d[mp[u]], ++d[mp[v]];
    }
    vector<bool> vis(tot);
    int sum = 0, cnt = 0;
    function <void(int)> dfs = [&] (int u) {
        vis[u] = true;
        sum += d[u];
        ++cnt;
        for (auto v : G[u]) {
            if (!vis[v]) {
                dfs(v);
            }
        }
    };
    int ans = 0;
    for (int i = 0; i < tot; i++) {
        if (!vis[i]) {
            cnt = sum = 0;
            dfs(i);
            sum /= 2;
            if (sum < cnt) {
                ans += sum;
            } else {
                ans += cnt;
            }
        }
    }
    cout << "Case #" << _ << ": ";
    cout << ans << '
';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    int T; cin >> T; while(T--)
    run();
    return 0;
}

K. Kabaleo Lite

直接贪心,但注意答案可能是(10^{19})会爆long long,然后施展各种奇技淫巧就行。

原文地址:https://www.cnblogs.com/heyuhhh/p/13456583.html