竞赛讨论区 > 【题解】"蔚来杯"2022牛客暑期多校训练营1
头像
王清楚
编辑于 2022-07-26 10:45
+ 关注

【题解】"蔚来杯"2022牛客暑期多校训练营1

补题写题解,即可得牛币https://ac.nowcoder.com/discuss/988926

A

首先简化一下题意,等价于有 个区间 ,定义两个区间连通当且仅当它们交集非空,你可以任意添加区间,求使得 个区间连通需要添加的最小区间长度和。

将所有区间按 排序,然后每个合并小区间得到的大区间可以表示为下标的一个连续段,填补大区间之间的空隙即可。

时间复杂度

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 9;
int ans, id[N], n, r[N], x[N];
int main() {
    cin >> n;

    for (int i = 1; i <= n; ++i) 
        id[i] = i, scanf("%d%d", &x[i], &r[i]);

    sort(id + 1, id + n + 1, [&](int a, int b) { return x[a] - r[a] < x[b] - r[b]; });

    for (int ii = 2, mx = x[id[1]] + r[id[1]]; ii <= n; ++ii) {
        int i = id[ii];
        if (x[i] - r[i] > mx)
            ans += x[i] - r[i] - mx;
        mx = max(mx, x[i] + r[i]);
    }

    printf("%d", ans);
    return 0;
}

C

首先对于 的行特判一下,他们会挡住后面的所有人。

一个点造成的影响就是 两个点到他的射线的交。

对于同一行的两个点 ,其中 ,第一个点的影响范围会完全包含第二个点,所以有用的点只有 个。

画图会发现,造成的影响是一个折线图。具体的,对于一行 ,会存在一个 表示 都合法, 都不合法。并且我们可以做两遍,第一次求出 对每行的影响,再求出 对每行的影响,每一行的 最终就是两次求得的较小值。

到此有个显然的做法就是直接李超树,但是时间复杂度无法接受,我们需要一个线性的做法。

到每个点为例。从小到大枚举每一行 ,然后比较已有射线和当前射线 (其中 表示第 行横坐标最小的点。),更新为斜率较大的一条,然后更新这一行的

时间复杂度

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 9;
int n, m, k, q;
int x1[N], mn[N];
int X[N], Y[N];
signed main() {
    cin >> n >> m >> k >> q;

    for (int i = 1; i <= k; i++)
        scanf("%lld%lld", &X[i], &Y[i]);

    while (q--) {
        int id;
        cin >> id;
        scanf("%lld%lld", &X[id], &Y[id]);

        for (int i = 1; i <= m; i++)
            x1[i] = n + 1, mn[i] = n;

        for (int i = 1; i <= k; i++)
            x1[Y[i]] = min(x1[Y[i]], X[i]);

        int j = 0;

        for (int i = 1; i <= m; i++) { //(0,1)
            // (0,1,x1[i],i) (0,1,x1[j],j)
            if (x1[i] != n + 1 && (!j || x1[i] * (j - 1) - (i - 1)*x1[j] < 0))
                j = i;

            if (j == 1)
                mn[i] = min(mn[i], i == 1 ? x1[i] - 1 : n);
            else
                mn[i] = min(mn[i], j ? ((i - 1) * x1[j] - 1) / (j - 1) : n);
        }

        j = 0;

        for (int i = m; i >= 1; i--) { //(0,m)
            // (0,m,x1[i],i) (0,m,x1[j],j)
            if (x1[i] != n + 1 && (!j || x1[i] * (j - m) - (i - m)*x1[j] > 0))
                j = i;

            if (j == m)
                mn[i] = min(mn[i], i == m ? x1[i] - 1 : n);
            else
                mn[i] = min(mn[i], j ? ((m - i) * x1[j] - 1) / (m - j) : n);
        }

        int ans = 0;

        for (int i = 1; i <= m; i++)
            ans += mn[i];

        cout << ans << endl;
    }

    return 0;
}

D

首先根据圆的对称性, 是等效的,于是只需考虑 的答案。

显然摧毁的墙壁是一段劣弧,那么长度的大小关系就可以映射到弦长的大小关系。设电磁炮摧毁墙壁的端点为 ,则对应弦长长度为 为定值,那么显然两点纵坐标之差的绝对值越大时弦长越大。

的两端点分别为 ,不妨设 ,令 值,则有 ,显然此时 垂直于 轴,即 ,弧长的计算使用极角相关知识即可。

#include <bits/stdc++.h>
using namespace std;
typedef long double ll;
int main() {
    int t_case;
    scanf("%d", &t_case);
    while (t_case--) {
        ll d, r, x, y;
        scanf("%Lf%Lf%Lf%Lf", &r, &x, &y, &d);
        ll p = sqrt(x * x + y * y);
        ll L = p - d, R = p + d;
        printf("%.10Lf\n", (acos(L / r) - acos(R / r)) * r);
    }
    return 0;
}

E

表示第一棵树上以 为根的子树和第二棵树上以 为根的子树的最大公共子序列的大小。

考虑更新 ,首先可以从所有的 取最大值继承过来。

,那么 可以匹配。只需要再将 的儿子和 的儿子两两匹配,得到最大权值。

每次更新都枚举 ,然后建立一条边 ,边权为 。跑一遍最大权匹配,用最后所得的答案 去更新 即可。

分析时间复杂度。假设左部点有 个,右部点有 个,且 ,增广的时间复杂度就是

那么时间复杂度就是 级别。

#include <bits/stdc++.h>
using namespace std;
const int N = 5e2 + 9;
const int INF = 1e9;
int c1[N], c2[N];
vector<int>e1[N], e2[N];
int dp[N][N], w[N][N];
int km(int n, int m) {
    vector<int>u(n + 1), v(m + 1), p(m + 1), Fa(m + 1);

    for (int i = 1; i <= n; i++) {
        p[0] = i;
        int a = 0;
        vector<int>mn(m + 1, INF);
        vector<bool>used(m + 1, false);

        do {
            used[a] = true;
            int i0 = p[a], del = INF, b = 0;

            for (int j = 1; j <= m; ++j)
                if (!used[j]) {
                    int val = w[i0][j] - u[i0] - v[j];

                    if (val < mn[j])
                        mn[j] = val, Fa[j] = a;

                    if (mn[j] < del)
                        del = mn[j], b = j;
                }

            for (int j = 0; j <= m; ++j)
                if (used[j])
                    u[p[j]] += del, v[j] -= del;
                else
                    mn[j] -= del;

            a = b;
        } while (p[a] != 0);

        do {
            int b = Fa[a];
            p[a] = p[b];
            a = b;
        } while (a);
    }

    return -v[0];
}
int n, m;
void dfs2(int x, int fx, int i, int fa) {
    for (auto to : e2[i])
        if (to != fa)
            dfs2(x, fx, to, i);

    for (auto to : e2[i])
        if (to != fa)
            dp[x][i] = max(dp[x][i], dp[x][to]);

    for (auto to : e1[x])
        if (to != fx)
            dp[x][i] = max(dp[x][i], dp[to][i]);

    if (c1[x] == c2[i]) {
        int f = (e1[x].size() <= e2[i].size());

        for (int j = 0; j < e1[x].size(); j++)
            for (int k = 0; k < e2[i].size(); k++) {
                int v = -dp[e1[x][j]][e2[i][k]] * (e1[x][j] != fx && e2[i][k] != fa);
                f ? (w[j + 1][k + 1] = v) : (w[k + 1][j + 1] = v);
            }

        if (f)
            dp[x][i] = max(dp[x][i], 1 - km(e1[x].size(), e2[i].size()));
        else
            dp[x][i] = max(dp[x][i], 1 - km(e2[i].size(), e1[x].size()));

    }
}
void dfs(int x, int fa) {
    for (auto to : e1[x])
        if (to != fa)
            dfs(to, x);

    dfs2(x, fa, 1, 0);
}
signed main() {
    cin >> n >> m;

    for (int i = 1; i <= n; i++)
        scanf("%d", &c1[i]);

    for (int i = 1; i <= m; i++)
        scanf("%d", &c2[i]);

    for (int i = 1; i < n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        e1[x].push_back(y);
        e1[y].push_back(x);
    }

    for (int i = 1; i < m; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        e2[x].push_back(y);
        e2[y].push_back(x);
    }

    dfs(1, 0);
    printf("%d\n", dp[1][1]);
    return 0;
}

F

先考虑如何快速做 操作。具体的,考虑如何快速合并两个有序的区间。动态开点值域线段树就非常方便。我们每次可以直接暴力合并。(当然,平衡树也可以)。

所以我们有一个做法,初始每个点都是一个区间,即一棵值域线段树。每次排序一个区间时,把完整在这个区间的进行合并。对于左右两端的,部分在区间内的线段树,我们使用线段树分裂分成两部分然后合并。

一个比较基础的询问就是询问区间和。具体地我们另开一个维护答案的数据结构。对于一整个区间,我们把这个区间的答案放在左端点上。每次询问区间,暴力查询两端散块,在维护答案的数据结构里查询整块答案。

那么对于这道题的询问,考虑没有 操作怎么快速维护。我们在线段树每个区间维护 表示起点奇偶性为 ,末尾奇偶性为 的最长答案。合并区间时合并答案。还有一个更方便的方法就是,一个区间 的答案就是这个区间的长度 减去相邻两个数奇偶性相同的个数。把这个东西同样的套到这道题上即可。

初始 个节点,每次分裂产生 个节点 ,时间复杂度

#include <bits/stdc++.h>
#define pi pair<int,int>
#define IT set<int>::iterator
using namespace std;
const int N = 1e5 + 9;
const int M = 1e7 + 9;
int read() {
    int f = 0, x = 0;
    char ch = getchar();

    while (!isdigit(ch)) {
        f |= (ch == '-');
        ch = getchar();
    }

    while (isdigit(ch)) {
        x = x * 10 + ch - '0';
        ch = getchar();
    }

    return f ? -x : x;
}
int n, m, a[N];
struct point {
    int sum;
    bool ok, vl, vr;
};
point operator+(point a, point b) {
    point res;
    res.ok = a.ok | b.ok;
    res.sum = a.sum + b.sum;
    res.sum += (a.vr == b.vl) && (a.ok && b.ok);
    res.vl = a.ok ? a.vl : b.vl;
    res.vr = b.ok ? b.vr : a.vr;
    return res;
}
struct tree {
#define ls p<<1
#define rs p<<1|1
    point t[N << 2];
    void add(int p, int l, int r, int nl, int x, pi y) {
        if (l == r) {
            t[p].ok = 1;
            t[p].sum = x;
            t[p].vl = y.first;
            t[p].vr = y.second;
            return;
        }

        int mid = l + r >> 1;

        if (nl <= mid)
            add(ls, l, mid, nl, x, y);
        else
            add(rs, mid + 1, r, nl, x, y);

        t[p] = t[ls] + t[rs];
    }
    void del(int p, int l, int r, int nl) {
        if (l == r) {
            t[p].ok = 0;
            t[p].sum = 0;
            t[p].vl = 0, t[p].vr = 0;
            return;
        }

        int mid = l + r >> 1;

        if (nl <= mid)
            del(ls, l, mid, nl);
        else
            del(rs, mid + 1, r, nl);

        t[p] = t[ls] + t[rs];
    }
    point get(int p, int l, int r, int nl, int nr) {
        if (l >= nl && r <= nr)
            return t[p];

        int mid = l + r >> 1;

        if (nr <= mid)
            return get(ls, l, mid, nl, nr);

        if (nl > mid)
            return get(rs, mid + 1, r, nl, nr);

        return get(ls, l, mid, nl, nr) + get(rs, mid + 1, r, nl, nr);
    }
#undef ls
#undef rs
} T;
int idx, rt[N];
int ls[M], rs[M], cnt[M], sum[M], tag[N];
bool vl[M], vr[M];
struct Tree {
    void push_up(int p) {
        sum[p] = sum[ls[p]] + sum[rs[p]];
        sum[p] += (vr[ls[p]] == vl[rs[p]]) && (ls[p] && rs[p]);
        vl[p] = ls[p] ? vl[ls[p]] : vl[rs[p]];
        vr[p] = rs[p] ? vr[rs[p]] : vr[ls[p]];
    }
    void add(int &p, int l, int r, int k) {
        cnt[p = ++idx] = 1;

        if (l == r) {
            vl[p] = vr[p] = k & 1;
            return;
        }

        int mid = (l + r) >> 1;
        k <= mid ? add(ls[p], l, mid, k) : add(rs[p], mid + 1, r, k);
        push_up(p);
    }
    int get(int p, int l, int r) {
        if (l == r)
            return l;

        int mid = (l + r) >> 1;
        return ls[p] ? get(ls[p], l, mid) : get(rs[p], mid + 1, r);
    }
    void merge(int &x, int y) {
        if (!(x && y)) {
            x |= y;
            return;
        }

        cnt[x] += cnt[y];
        merge(ls[x], ls[y]);
        merge(rs[x], rs[y]);
        push_up(x);
    }
    void split(int &p1, int p2, int k, int tag) {
        if (cnt[p2] == k)
            return;

        cnt[p1 = ++idx] = cnt[p2] - k;
        cnt[p2] = k;

        if (tag) {
            if (k <= cnt[rs[p2]])
                split(rs[p1], rs[p2], k, tag),
                      ls[p1] = ls[p2], ls[p2] = 0;
            else
                split(ls[p1], ls[p2], k - cnt[rs[p2]], tag);
        } else {
            if (k <= cnt[ls[p2]])
                split(ls[p1], ls[p2], k, tag),
                      rs[p1] = rs[p2], rs[p2] = 0;
            else
                split(rs[p1], rs[p2], k - cnt[ls[p2]], tag);
        }

        push_up(p1);
        push_up(p2);
    }
} G;
set<int>t;
IT work(int p) {
    auto it = t.lower_bound(p);

    if (*it == p)
        return it;

    --it;
    int x = *it;
    G.split(rt[p], rt[x], p - x, tag[p] = tag[x]);
    T.del(1, 1, n, x);
    pi res = make_pair(vl[rt[x]], vr[rt[x]]);

    if (tag[p])
        swap(res.first, res.second);

    T.add(1, 1, n, x, sum[rt[x]], res);
    res = make_pair(vl[rt[p]], vr[rt[p]]);

    if (tag[p])
        swap(res.first, res.second);

    T.add(1, 1, n, p, sum[rt[p]], res);
    return t.insert(p).first;
}
int main() {
    n = read(), m = read();
    t.insert(n + 1);

    for (int i = 1; i <= n; i++) {
        G.add(rt[i], 1, n, a[i] = read());
        t.insert(i);
        T.add(1, 1, n, i, 0, make_pair(a[i] & 1, a[i] & 1));
    }

    for (int i = 1; i <= m; i++) {
        int op = read() - 1, l = read(), r = read();

        if (op < 2) {
            auto L = work(l), R = work(r + 1);

            for (auto i = ++L; i != R; ++i)
                G.merge(rt[l], rt[*i]);

            tag[l] = op;
            pi res = make_pair(vl[rt[l]], vr[rt[l]]);

            if (op)
                swap(res.first, res.second);

            T.add(1, 1, n, l, sum[rt[l]], res);

            for (auto it = L; it != R; ++it)
                T.del(1, 1, n, *it);

            t.erase(L, R);
        } else {
            work(l), work(r + 1);
            printf("%d\n", r - l + 1 - T.get(1, 1, n, l, r).sum);
        }
    }
}

G

从高到低贪心放 ,那么答案有两种可能:

除了最后一位都是 ,答案就是 ,否则答案是

#include <bits/stdc++.h>
using namespace std;
int main(){
    string s;
    cin >> s;
    cout << max(s,string( s.size() - 1 ,'9'));
}

H

因为限制是对于某一个二进制位,完全背包就很麻烦。这启发考虑按位处理。

具体的,类似于二进制分组优化的想法,把一个物品拆成 份,从小到大枚举二进制位,每一位暴力做 背包。因为做到第 位的时候,加上的数是 的倍数,更低位的数是没有贡献的。于是可以大胆的弃掉。

表示做到第 位,现在背包体积 的方案数。

每次暴力 背包的复杂度显然不能接受。观察每次乘上去的式子 其中 表示二进制第 位被 掉的位置。他也等价于:
$\rm NTT$ 实现。

考虑到 不同的最多只有 个,意思是不同的二项式只有根号个。我们可以暴力求出点值,然后 回去,得到原本的多项式。相比分治 这个非常好写。

然后再暴力除掉下面的每个二项式,这相当于退掉 背包。因为总共的限制只有 个,每次退的时间复杂度是 ,和 同阶。所以这部分的时间复杂度是

题目要求体积小于等于 的方案数,我们直接乘上一个 转化为求恰好,那么每次进位只能保留和 二进制奇偶相同的体积。即 。进位最多进 ,数组只需要开到 即可。

#include <bits/stdc++.h>
using namespace std;
const int p = 998244353;
const int N = 1e5 + 5e4;
const int G = 3, Gi = 332748118;
const int lim = 40001;
int inv(int x, int base = p - 2) {
    int res = 1;

    while (base) {
        if (base & 1)
            res = 1ll * res * x % p;

        x = 1ll * x * x % p;
        base >>= 1;
    }

    return res;
}
int r[N << 1];
inline int get(int x) {
    int len = 1;

    while (len <= x)
        len <<= 1;

    return len;
}
void NTT(int *A, int len, int tag) {
    for (int i = 1; i <= len; i++)
        r[i] = (r[i >> 1] >> 1) | ((i & 1) ? (len >> 1) : 0);

    for (int i = 0; i < len; i++)
        if (i < r[i])
            swap(A[i], A[r[i]]);

    for (int mid = 1; mid < len; mid <<= 1) {
        int wn = inv(tag ? G : Gi, (p - 1) / (mid << 1));

        for (int j = 0; j < len; j += (mid << 1))
            for (int k = 0, w = 1; k < mid; k++, w = 1ll * w * wn % p) {
                int x = A[j + k], y = 1ll * w * A[j + k + mid] % p;
                A[j + k] = (x + y) % p;
                A[j + k + mid] = (x + p - y) % p;
            }
    }

    if (tag)
        return;

    int invlen = inv(len);

    for (int i = 0; i < len; i++)
        A[i] = 1ll * A[i] * invlen % p;
}
int a[N], A[N], F[N], g[N];
int n, K, cnt[N];
long long m;
vector<int>had[62];
int vis[N], tag;
signed main() {
    scanf("%d%lld%d", &n, &m, &K);
    a[0] = 1;
    ++cnt[1];

    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]), ++cnt[a[i]];

    for (int i = 1; i <= K; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        had[y].push_back(x);
    }

    int len = get(lim);
    int w = inv(3, (p - 1) / len);
    A[0] = 1;

    for (int i = 1; i < len; i++)
        A[i] = 1ll * A[i - 1] * w % p;

    for (int i = 0; i < len; i++)
        F[i] = 1;

    for (int i = 0; i <= lim; i++)
        if (cnt[i])
            for (int j = 0; j < len; j++)
                F[j] = 1ll * F[j] * inv((A[(j * i) & (len - 1)] + 1) % p, cnt[i]) % p;

    NTT(F, len, 0);
    len = get(2 * lim);
    memset(A, 0, sizeof(A));
    A[0] = 1;

    for (int i = 0; (i <= 60) && m; i++) {
        memcpy(g, F, sizeof(F));
        ++tag;

        for (auto x : had[i])
            if (vis[x] != tag) {
                vis[x] = tag;

                for (int j = a[x]; j <= lim; j++)
                    g[j] = (g[j] + p - g[j - a[x]]) % p;
            }

        NTT(A, len, 1);
        NTT(g, len, 1);

        for (int j = 0; j < len; j++)
            g[j] = 1ll * g[j] * A[j] % p;

        NTT(g, len, 0);
        memset(A, 0, sizeof(A));

        for (int j = m % 2; j <= 2 * lim; j += 2)
            A[j >> 1] = g[j];

        m >>= 1;
    }

    printf("%d", A[0]);
    return 0;
}

I

题目有个条件就是,初始手牌每一种最多两张。那么最优策略就是,只要新摸到的牌能和手中的单牌凑出对子,就留着,打出一张单牌;否则打出摸到的牌。

那么就可以考虑一个暴力的 表示已经摸了 张牌,手上有 张单牌还需进行的期望轮数。

首先牌库里还有的牌有

若摸到的是手中的单牌,期望为 ,否则为

注意,手中的单牌在牌库里一定剩下 张,否则一定在之前凑一起了。

其中手中只剩一张牌并且摸到了需要的要特判转移,或者记忆化搜索。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int p = 1e9 + 7;
int dp[200][14];
int inv(int x, int base = p - 2) {
    int res = 1;

    while (base) {
        if (base & 1)
            res = 1ll * res * x % p;

        x = 1ll * x * x % p;
        base >>= 1;
    }

    return res;
}
//136-13=123
char s[100];
int sta[100], top;
int dfs(int a, int c) {
    if (c == -1) {
        return 0;
    }

    if (dp[a][c] != -1)
        return dp[a][c];

    int res = 0;

    if (123 - a - c * 3)
        res += 1ll * (123 - a - c * 3) * inv(123 - a) % p * dfs(a + 1, c) % p;

    res += 1ll * (3 * c) * inv(123 - a) % p * dfs(a + 1, c - 2) % p;
    return dp[a][c] = (res + 1) % p;

}
signed main() {
    int q;
    cin >> q;
    memset(dp, -1, sizeof dp);

    for (int k = 1; k <= q; k++) {
        scanf("%s", s + 1);

        for (int i = 1; i <= 13; i++)
            sta[i] = (s[i * 2 - 1] - '0') * 100 + (s[i * 2] - 'a');

        sort(sta + 1, sta + 1 + 13);
        int x = 0;

        for (int i = 1; i <= 12; i++)
            if (sta[i] == sta[i + 1])
                x++;

        printf("Case #%d: %lld\n", k, (dfs(0, 13 - 2 * x)) % p);
    }

    return 0;
}

J

首先可以观察出,若两个点的答案集合有交集,则这两个集合一定是包含关系(严谨证明见出题人sol),因此我们可以尝试将每个点的后继合并到这个点,最后输出最大的集合大小。

暴力合并的复杂度显然是无法接受的,因此考虑如下技巧:

若该后继的某个前驱已经在当前点的答案集合,则将该前驱直接从后继的前驱集合中删去。否则说明当前点无法被合并,直接退出循环。最后将当前点插入后继的前驱集合。这样可以避免无意义查询,使得点 的遍历次数是 的。

但是合并后继时貌似有点问题:我们会将后继的后继当成新的后继,若我们构造一个链套菊花图,使得菊花图部分的点都无法被合并但链部分的点都能被合并,则菊花图部分的点会被加入链长次,这样时间复杂度就被卡到了

解决方案当然是有的,若我们从链顶开始做,就能避免这一情况,因此我们判断若当前点有唯一前驱且前驱编号比自己大(避免在出现环时出错),则不处理当前点,等到处理前驱时再考虑。

时间复杂度也许是

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct dsu {
    int fa[N], siz[N];
    inline void init(int k) {
        for (int i = 1; i <= k; ++i) fa[i] = i, siz[i] = 1;
    }
    int find(int k) { return k == fa[k] ? k : fa[k] = find(fa[k]); }
    inline bool merge(int x, int y) {
        x = find(x), y = find(y);
        if (x != y) {
            fa[x] = y, siz[y] += siz[x];
            return true;
        }
        return false;
    }
} d;
int deg[N], n, num, t_case;
set<int> fa[N], to[N];
bool vis[N];
int main() {
    scanf("%d", &t_case);
    while (t_case--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) fa[i].clear(), to[i].clear();
        for (int i = 1; i <= n; ++i) {
            scanf("%d", deg + i);
            for (int j = 1, x; j <= deg[i]; ++j) {
                scanf("%d", &x);
                fa[i].insert(x);
                to[x].insert(i);
            }
        }
        int ans = 0;
        d.init(n);
        for (int i = 1; i <= n; ++i)
            if (d.find(i) == i) {
                if (fa[i].size() == 1 && *fa[i].begin() > i)
                    continue;
                auto update = [&](int k) {
                    for (auto it = to[k].begin(); it != to[k].end(); ++it)
                        if (d.find(*it) != *it)
                            it = to[k].erase(it);
                };
                update(i);
                queue<int> q;
                for (int j : to[i]) q.push(j);
                while (q.size()) {
                    int p = q.front();
                    q.pop();
                    if (!to[i].count(p))
                        continue;
                    for (auto it = fa[p].begin(); it != fa[p].end();) {
                        if (d.find(*it) == i)
                            it = fa[p].erase(it);
                        else {
                            to[i].insert(p);
                            goto skip;
                        }
                    }
                    d.merge(p, i);
                    to[i].erase(p);
                    update(p);
                    for (int j : to[p])
                        if (i != j)
                            q.push(j), to[i].insert(j);
                skip:;
                    fa[p].insert(i);
                }
                ans = max(ans, d.siz[i]);
            }
        printf("Case #%d: %d\n", ++num, ans);
    }
    return 0;
}

全部评论

(2) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐