【比赛题解】CSP2019 简要题解

廉价感情. 提交于 2019-12-05 04:58:15

D1T1 code

签到题,大家都会。

可以从高位往低位确定,如果遇到 \(1\),则将排名取反一下。

注意要开 unsigned long long

#include <bits/stdc++.h>

typedef unsigned long long u64; 

const int MaxN = 100; 

u64 n, K; 
bool ans[MaxN]; 

inline void solve(u64 dep, u64 k)
{
    if (dep == 0)
        return; 
    
    u64 lsze = 1ull << (dep - 1); 
    if (k < lsze)
    {
        ans[dep] = false; 
        solve(dep - 1, k); 
    }
    else
    {
        ans[dep] = true; 
        solve(dep - 1, lsze - (k - lsze) - 1); 
    }
}

int main()
{
    freopen("code.in", "r", stdin); 
    freopen("code.out", "w", stdout); 
    
    std::cin >> n >> K; 
    
    solve(n, K); 
    
    for (int i = n; i >= 1; --i)
        putchar(ans[i] ? '1' : '0'); 
    
    return 0; 
}

D1T2 brackets

简单题,大家都会。

大家的做法都好巨,我只会奇奇怪怪的做法。

考虑每次加一个括号之后答案的增量,显然只有加右括号的时候答案会增加。

我们记两个量, \(lst\_lef_i\) 表示 \(i\to 1\) 的路径上最后一个没有匹配的左括号,\(lst\_blk_i\) 表示以 \(i\) 结尾的合法子串个数(本质上是数出类似这种串 ...(...)(...)(...)(...) 的极长合法后缀可以分成几段 (...))。

这两个量可以直接线性推出来,然后就做完了。时间复杂度 \(O(n)\)

#include <bits/stdc++.h>

template <class T>
inline void read(T &x)
{
    static char ch; 
    while (!isdigit(ch = getchar())); 
    x = ch - '0'; 
    while (isdigit(ch = getchar()))
        x = x * 10 + ch - '0'; 
}

typedef long long s64; 

const int MaxNV = 5e5 + 5; 
const int MaxNE = MaxNV; 

int n; 
int fa[MaxNV]; 
char s[MaxNV]; 

int lst_lef[MaxNV]; 
int lst_blk[MaxNV]; 

s64 ans[MaxNV], xor_ans; 

int main()
{
    freopen("brackets.in", "r", stdin); 
    freopen("brackets.out", "w", stdout); 
    
    scanf("%d%s", &n, s + 1); 
    for (int i = 2; i <= n; ++i)
        read(fa[i]); 
    
    if (s[1] == '(')
        lst_lef[1] = 1; 
    
    for (int u = 2; u <= n; ++u)
    {
        ans[u] = ans[fa[u]]; 
        
        if (s[u] == '(')
        {
            lst_lef[u] = u; 
            lst_blk[u] = 0; 
        }
        else
        {
            if (lst_lef[fa[u]])
            {
                int lef_u = lst_lef[fa[u]]; 
                
                lst_lef[u] = lst_lef[fa[lef_u]]; 
                lst_blk[u] = lst_blk[fa[lef_u]] + 1; 
                ans[u] += lst_blk[u]; 
            }
            else
            {
                lst_lef[u] = 0; 
                lst_blk[u] = 0; 
            }
        }
        xor_ans ^= 1LL * u * ans[u]; 
    }
    
    std::cout << xor_ans << std::endl; 
    
    return 0; 
}

D1T3 tree

细节题。这个题的 idea 挺好的,就是容易分类讨论挂。

难度其实不大,放在 D1T3 其实没啥毛病。可能出题人高估了我的代码能力。我太菜了,考场上调不出来。

一个显然的贪心就是从小到大枚举数字,然后判断这个数字最终能送到那个位置。显然每次我们都贪心地选取最小的位置。

考虑一条路径 \(u_1\to u_2\to \dots \to u_k\),假设我们要将 \(u_1\) 的原来数字送到 \(u_k\),那么需要满足下列条件:

  • \((u_1,u_2)\) 是和 \(u_1\) 相连的所有边中第一个被删除的
  • \((u_{k - 1}, u_k)\) 是和 \(u_k\) 相连的所有边中最后一个被删除的
  • \((u_{i-1},u_i)\) 必须比 \((u_i,u_{i+1})\) 先删除,并且在删除 \((u_{i-1},u_i)\) 后,删除 \((u_i,u_{i+1})\) 之前,不能有和 \(u_i\) 相连的其他边被删除。

那么我们就对每个点,维护出与其相连的所有边的限制。这些限制具体可以用一个链表来表示,并且需要记录每个点强制限制的第一个删除的边,和最后一个删除的边。

实现的时候,就从当前枚举的数字所在的点开始 dfs,显然满足第一个和第三个条件的点构成一个联通块。我们只需要在 dfs 的时候顺带判断这些条件能否满足即可。

时间复杂度 \(O(n^2)\)。细节比较多,我是根据考场的混乱思路瞎写的,相信读者一定有比我更优秀的实现方法。

#include <bits/stdc++.h>

template <class T>
inline void read(T &x)
{
    static char ch; 
    while (!isdigit(ch = getchar())); 
    x = ch - '0'; 
    while (isdigit(ch = getchar()))
        x = x * 10 + ch - '0'; 
}

template <class T>
inline void putint(T x)
{
    static char buf[25], *tail = buf; 
    if (!x)
        putchar('0'); 
    else
    {
        for (; x; x /= 10) *++tail = x % 10 + '0'; 
        for (; tail != buf; --tail) putchar(*tail); 
    }
}

const int MaxN = 2e3 + 5; 

int n; 
int idx[MaxN], col[MaxN], fa[MaxN]; 
int adj[MaxN][MaxN], deg[MaxN]; 

int ans[MaxN]; 
int fir[MaxN], lst[MaxN]; 
int head[MaxN][MaxN], sze[MaxN][MaxN]; 
int pre[MaxN][MaxN], suf[MaxN][MaxN]; 

inline void init()
{
    read(n); 
    for (int i = 1; i <= n; ++i)
    {
        fir[i] = lst[i] = 0; 
        deg[i] = ans[i] = fa[i] = 0; 

        for (int j = 1; j <= n; ++j)
        {
            head[i][j] = j; 
            sze[i][j] = 1; 
            pre[i][j] = suf[i][j] = 0; 
        }
    }

    for (int i = 1; i <= n; ++i)
    {
        read(idx[i]); 
        col[idx[i]] = i; 
    }

    for (int i = 1; i < n; ++i)
    {
        int u, v; 
        read(u), read(v); 
        adj[u][++deg[u]] = v; 
        adj[v][++deg[v]] = u; 
    }
}

inline void dfs(int u, int src)
{
    if (u != src)
    {
        bool flg = true; 

        if (deg[u] != 1)
        {
            flg &= fir[u] != fa[u] && !suf[u][fa[u]]; 
            flg &= !lst[u] || lst[u] == fa[u]; 
            if (fir[u] && head[u][fir[u]] == head[u][fa[u]])
                flg &= sze[u][head[u][fa[u]]] == deg[u]; 
        }

        if (flg)
        {
            if (!ans[src] || u < ans[src])
                ans[src] = u; 
        }
    }
    for (int i = 1; i <= deg[u]; ++i)
    {
        int v = adj[u][i]; 
        if (v == fa[u])
            continue; 

        fa[v] = u; 

        bool flg = true; 
        if (u == src)
        {
            if (deg[u] != 1)
            {
                flg &= lst[u] != v && !pre[u][v]; 
                if (lst[u] && head[u][lst[u]] == head[u][v])
                    flg &= sze[u][head[u][v]] == deg[u]; 
            }
        }
        else
        {
            flg &= !suf[u][fa[u]] || suf[u][fa[u]] == v; 
            flg &= !pre[u][v] || pre[u][v] == fa[u]; 
            flg &= suf[u][fa[u]] == v || head[u][v] != head[u][fa[u]]; 
            flg &= head[u][fir[u]] != head[u][v] && head[u][lst[u]] != head[u][fa[u]]; 
            if (head[u][lst[u]] == head[u][v] && head[u][fir[u]] == head[u][fa[u]])
                flg &= sze[u][head[u][lst[u]]] + sze[u][head[u][fir[u]]] == deg[u]; 
        }

        if (flg)
            dfs(v, src); 
    }
}

inline void modify(int x, int src)
{
    if (!x)
        return; 

    lst[x] = fa[x]; 

    int y = x; 
    while (fa[y] != src)
    {
        suf[fa[y]][fa[fa[y]]] = y; 
        pre[fa[y]][y] = fa[fa[y]]; 

        if (head[fa[y]][fa[fa[y]]] != head[fa[y]][y])
        {
            int l = head[fa[y]][fa[fa[y]]]; 
            int z = y; 
            while (z)
            {
                ++sze[fa[y]][l]; 
                head[fa[y]][z] = l; 
                z = suf[fa[y]][z]; 
            }
        }
        y = fa[y]; 
    }

    fir[src] = y; 
}

inline void solve()
{
    for (int c = 1; c <= n; ++c)
    {
        int u = idx[c]; 
        if (n == 1)
        {
            puts("1"); 
            continue; 
        }

        fa[u] = 0; 
        dfs(u, u); 
        modify(ans[u], u); 

        putint(ans[u]); 
        putchar(" \n"[c == n]); 
    }
}

int main()
{
    freopen("tree.in", "r", stdin); 
    freopen("tree.out", "w", stdout); 
    
    int orzczk; 
    read(orzczk); 

    while (orzczk--)
    {
        init(); 
        solve(); 
    }

    return 0; 
}

D2T1 meal

简单题,就我不会。考场上降智太严重了,会了 \(O(mn^3)\) 竟然不会 \(O(mn^2)\)。我校其他选手全部 AC 此题,水平高下立判。

因为如果有食材超过一半,那么最多只能有一个这样的食材,所以不难想到用总的方案数减去有一个主要食材超过一半的方案数。总的方案数就是
\[ \prod_{i=1}^n\left(1+\sum_{j=1}^ma_{i,j}\right)-1 \]
减一是因为不能一个都不选。

考虑如何限制某个食材超过一半,显然我们可以考虑枚举这个食材,然后把用这个食材的菜权值看成 \(+1\),不用这个食材的菜权值看成 \(-1\),那么相当于选的所有菜的总权值要大于 \(0\)

具体地,我们可以用一个背包 DP 实现。显然大家都会,就不讲了。

\(f(i,j)\) 表示前 \(i\) 种方法,选的菜的总权值为 \(j\) 的方案数。

假设现在限制的是第 \(p\) 种食材,那么转移非常显然:
\[ \begin{aligned} f(i,j) &\leftarrow f(i-1,j-1)\times a_{i,p}\\ f(i,j) &\leftarrow f(i-1,j+1)\times \sum_{j \neq p}a_{i,j} \end{aligned} \]
为了避免负数下标,可以加一个常数。时间复杂度 \(O(mn^2)\)

#include <bits/stdc++.h>

template <class T>
inline void read(T &x)
{
    static char ch; 
    while (!isdigit(ch = getchar())); 
    x = ch - '0'; 
    while (isdigit(ch = getchar()))
        x = x * 10 + ch - '0'; 
}

const int MaxN = 1e2 + 5; 
const int MaxM = 2e3 + 5; 
const int mod = 998244353; 

int n, m; 
int a[MaxN][MaxM], sum[MaxN]; 

int f[MaxN][MaxN << 1]; 

inline void add(int &x, const int &y)
{
    x += y; 
    if (x >= mod)
        x -= mod; 
}

inline void dec(int &x, const int &y)
{
    x -= y; 
    if (x < 0)
        x += mod; 
}

inline int minus(int x, const int &y)
{
    x -= y; 
    return x < 0 ? x + mod : x; 
}

int main()
{
    freopen("meal.in", "r", stdin); 
    freopen("meal.out", "w", stdout); 

    read(n), read(m); 
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= m; ++j)
        {
            read(a[i][j]); 
            add(sum[i], a[i][j]); 
        }
    }

    int ans = 1; 
    for (int i = 1; i <= n; ++i)
        ans = 1LL * ans * (sum[i] + 1) % mod; 
    
    dec(ans, 1); 
    for (int p = 1; p <= m; ++p)
    {
        f[0][n] = 1; 
        for (int i = 1; i <= n; ++i)
            for (int j = 0; j <= (n << 1); ++j)
            {
                f[i][j] = f[i - 1][j]; 
                if (j > 0)
                    add(f[i][j], 1LL * f[i - 1][j - 1] * a[i][p] % mod); 
                if (j < (n << 1))
                    add(f[i][j], 1LL * f[i - 1][j + 1] * minus(sum[i], a[i][p]) % mod); 
            }

        for (int i = 1; i <= n; ++i)
            dec(ans, f[n][i + n]); 
    }

    printf("%d\n", ans); 

    return 0; 
}

D2T2 partition

打表找规律题,考场上来不及了。

开始我们有一个显然的 DP 是,设 \(f(i,j)\) 表示前 \(i\) 个数,最后一段是 \([j+1,i]\),的最小平方和。这样的 DP 实现地优秀一点可以做到 \(O(n^2)\)

强烈的感觉告诉我们,这题有奇妙结论,考场上当然是打表。打表后不难发现在合法范围内,\(f(i,j)\)\(j\) 单调递减。

证明参考出题人myy的博客: http://matthew99.blog.uoj.ac/blog/5299

因此我们对于每个 \(i\) 只要记一个最优决策点即可,每个 \(i\) 的决策点一定是取在最靠右的合法位置。记这个位置为 \(p_i\)。考虑一个决策点 \(j(j<i)\) 是合法的当且仅当 \(s_i-s_j\geq s_j-s_{p_j}\),其中 \(s_i\) 表示 \(1\dots i\) 的前缀和。

移项一下这个条件就是 \(2s_j-s_{p_j} \leq s_i\),到这里可以用一个BIT 做到 \(O(n \log n)\)

但是实际上,不难发现 \(s_i\) 是单增的,也就是说一个决策点 \(j\) 对于当前的 \(i\) 是合法的,那么对于后面的肯定仍是合法的。于是我们考虑维护一个\(2s_j-s_{p_j}\) 单增的单调队列。对于每个 \(i\) 就从队头开始找到最后一个合法决策点,再将 \(i\) 插到队尾,并且将队尾的那些 \(2s_j-s_{p_j}\geq 2s_i-s_{p_i}\) 的决策点弹掉。

这样的时间复杂度就是 \(O(n)\) 了。

本题还有个问题就是,你不能一边做这个 DP 一边算这个 DP 值,得把 \(p_i\) 存起来最后算(因为空间不够)。高精还需要压位/二进制。

当然,你在 OJ 上用 __int128 也是可以的。

#include <bits/stdc++.h>

template <class T>
inline void read(T &x)
{
    static char ch; 
    static bool opt; 
    while (!isdigit(ch = getchar()) && ch != '-'); 
    x = (opt = ch == '-') ? 0 : ch - '0'; 
    while (isdigit(ch = getchar()))
        x = x * 10 + ch - '0'; 
    if (opt)
        x = ~x + 1; 
}

template <class T>
inline void putint(T x)
{
    static char buf[45], *tail = buf; 
    if (!x)
        putchar('0'); 
    else
    {
        if (x < 0)
        {
            putchar('-'); 
            x = ~x + 1; 
        }
        for (; x; x /= 10) *++tail = x % 10 + '0'; 
        for (; tail != buf; --tail) putchar(*tail); 
    }
}

typedef long long s64; 

const int MaxN = 4e7 + 5; 
const s64 mod = 1e9; 

int n, type, ql, qr; 
int que[MaxN], maxp[MaxN], b[MaxN]; 
s64 s[MaxN]; 

struct bignum
{
    int len; 
    s64 a[7]; 
    bignum(){}
    bignum(s64 t)
    {
        len = 1; 
        memset(a, 0, sizeof(a)); 

        a[1] = t % mod; 
        if (t >= mod)
            a[++len] = t / mod; 
    }

    inline void operator += (const bignum &rhs)
    {
        len = std::max(len, rhs.len); 
        for (int i = 1; i <= len; ++i)
        {
            a[i] += rhs.a[i]; 
            a[i + 1] += a[i] / mod; 
            a[i] %= mod; 
        }
        if (a[len + 1])
            ++len; 
    }

    inline bignum operator * (const bignum &rhs) const
    {
        bignum res(0); 
        res.len = len + rhs.len; 
        for (int i = 1; i <= len; ++i)
            for (int j = 1; j <= rhs.len; ++j)
                res.a[i + j - 1] += a[i] * rhs.a[j]; 
        for (int i = 1; i < res.len; ++i)
        {
            res.a[i + 1] += res.a[i] / mod; 
            res.a[i] %= mod; 
        }
        while (res.len > 1 && !res.a[res.len])
            --res.len; 
        return res; 
    }

    inline void print()
    {
        printf("%d", (int)a[len]); 
        for (int i = len - 1; i >= 1; --i)
            printf("%09d", (int)a[i]); 
    }
}res(0); 

inline s64 calc(int x)
{
    return 2 * s[x] - s[maxp[x]]; 
}

int main()
{
    freopen("partition.in", "r", stdin); 
    freopen("partition.out", "w", stdout); 

    read(n), read(type); 
    if (type == 0)
    {
        for (int i = 1; i <= n; ++i)
        {
            int x; 
            read(x); 
            s[i] = s[i - 1] + x; 
        }
    }
    else
    {
        int x, y, z, m; 
        read(x), read(y), read(z), read(b[1]), read(b[2]), read(m); 
        for (int i = 1, lstp = 0; i <= m; ++i)
        {
            int p, l, r; 
            read(p), read(l), read(r); 
            for (int j = lstp + 1; j <= p; ++j)
            {
                if (j > 2)
                    b[j] = (1LL * x * b[j - 1] + 1LL * y * b[j - 2] + z) % (1 << 30); 
                s[j] = s[j - 1] + b[j] % (r - l + 1) + l; 
            }
            lstp = p; 
        }
    }
    
    que[ql = qr = 1] = 0; 
    for (int i = 1; i <= n; ++i)
    {
        while (ql < qr && calc(que[ql + 1]) <= s[i])
            ++ql; 
        maxp[i] = que[ql]; 
        while (ql <= qr && calc(que[qr]) >= calc(i))
            --qr; 
        que[++qr] = i; 
    }

    int cur = n; 
    while (cur)
    { 
        res += bignum(s[cur] - s[maxp[cur]]) * bignum(s[cur] - s[maxp[cur]]); 
        cur = maxp[cur]; 
    }

    res.print(); 
    
    return 0; 
}

D2T3 centroid

不难题,就我不会。因为这是 D2T3 所以我只想着写暴力,实际上这题挺简单的。

将题目中的计算方式转化为:对于每个点,计算它成为重心的方案数。

那么考虑一个点 \(u\),将其硬点为,对于它的每个儿子 \(v\),相当于在 \(v\) 的子树再选出一个大小为 \(s_0\) 的子树,这个 \(s_0\) 需要在一个范围。具体地,我们记 \(s'\) 表示 \(u\) 除了 \(v\) 以外的其他儿子的最大子树大小,记 \(s_v\) 表示 \(v\) 的子树大小。

那么就有
\[ s_v-s_0 \leq \left\lfloor\frac {n-s_0}2\right\rfloor \\ s_0 \geq 2s_v-n \]
以及
\[ s^\prime\leq \left\lfloor \frac {n-s_0}2\right\rfloor\\ s_0\leq n-2s^\prime \]
那么就要求 \(s_0\in [2s_v-n,n-2s^\prime]\)

对应到原树(钦定 \(1\) 为根的有根树)上,这相当于分两类讨论:

  1. \(v\)\(u\) 的一个儿子,这个时候直接在 dfs 时用一个 BIT 存下当前结点到根的路径上所有点的询问区间对应的贡献,然后遍历到一个点的时候在 BIT 上单点查询即可。
  2. \(v\)\(u\) 的父亲,这时候的统计比较复杂,需要分成几块统计。
    • 原树中不在 \(u\) 子树中且不在 \(u\) 到根路径上的结点的 \(size\in [2(n-s_u)-n,n-2s^\prime]\),这个可以用整棵树\(size\) 在这个区间的点数减去子树内 \(size\) 在这个区间的点数,再减去到根结点的路径上的点 \(size\) 在这个区间的点数。子树询问可以归到第一类的 BIT 中,到根结点的可以另外再维护一个 BIT。
    • 原树中 \(u\) 到根的路径上,向父亲方向的「子树」。这个可以 dfs 的时候归到上面第二个 BIT 维护。

那么这样我们就可以用 BIT 来做了。时间复杂度 \(O(n\log n)\),常数有点大,可能打不过线段树选手。

#include <bits/stdc++.h>

template <class T>
inline void read(T &x)
{
    static char ch; 
    while (!isdigit(ch = getchar())); 
    x = ch - '0'; 
    while (isdigit(ch = getchar()))
        x = x * 10 + ch - '0'; 
}

template <class T>
inline void putint(T x)
{
    static char buf[25], *tail = buf; 
    if (!x)
        putchar('0'); 
    else
    {
        for (; x; x /= 10) *++tail = x % 10 + '0'; 
        for (; tail != buf; --tail) putchar(*tail); 
    }
}

template <class T>
inline void relax(T &x, const T &y)
{
    if (x < y)
        x = y; 
}

template <class T>
inline void tense(T &x, const T &y)
{
    if (x > y)
        x = y; 
}

typedef long long s64; 

const int MaxNV = 3e5 + 5; 
const int MaxNE = MaxNV << 1; 

int n; 
int ect, adj[MaxNV]; 
int nxt[MaxNE], to[MaxNE]; 

int fa[MaxNV], sze[MaxNV]; 
s64 ans, sum[MaxNV], bit_q[MaxNV], bit_u[MaxNV]; 

int max_sze[MaxNV][2]; 

#define trav(u) for (int e = adj[u], v; v = to[e], e; e = nxt[e])

inline void addEdge(int u, int v)
{
    nxt[++ect] = adj[u]; 
    adj[u] = ect; 
    to[ect] = v; 
}

inline void init()
{
    read(n); 

    ect = 0; 
    ans = 0; 
    for (int i = 1; i <= n; ++i)
    {
        adj[i] = sum[i] = 0; 
        bit_q[i] = bit_u[i] = 0; 
        max_sze[i][0] = max_sze[i][1] = 0; 
    }

    for (int i = 1; i < n; ++i)
    {
        int u, v; 
        read(u), read(v); 
        addEdge(u, v); 
        addEdge(v, u); 
    }
}

inline void bit_modify(int x, int val, s64 *bit)
{
    for (; x <= n; x += x & -x)
        bit[x] += val; 
}

inline s64 bit_query(int x, s64 *bit)
{
    s64 res = 0; 
    for (; x; x ^= x & -x)
        res += bit[x]; 
    return res; 
}

inline void seg_modify(int l, int r, int val, s64 *bit)
{
    relax(l, 1), tense(r, n); 
    if (r < l) return; 

    bit_modify(l, val, bit); 
    bit_modify(r + 1, -val, bit); 
}

inline s64 seg_query(int l, int r, s64 *bit)
{
    relax(l, 1), tense(r, n); 
    if (r < l) return 0; 
    return bit_query(r, bit) - bit_query(l - 1, bit); 
}

inline void upt(int x, int s)
{
    if (s >= max_sze[x][0])
    {
        max_sze[x][1] = max_sze[x][0]; 
        max_sze[x][0] = s; 
    }
    else
        relax(max_sze[x][1], s); 
}

inline int max_else(int x, int s)
{
    if (s == max_sze[x][0])
        return max_sze[x][1]; 
    return max_sze[x][0]; 
}

inline void dfs_init(int u)
{
    sze[u] = 1; 
    trav(u)
        if (v != fa[u])
        {
            fa[v] = u; 
            dfs_init(v); 

            sze[u] += sze[v]; 
            upt(u, sze[v]); 
        }
    upt(u, n - sze[u]); 
}

inline void dfs(int u)
{
    int ul = std::max(1, 2 * (n - sze[u]) - n); 
    int ur = std::min(n, n - 2 * max_else(u, n - sze[u])); 
    if (ul <= ur)
        ans += (sum[ur] - sum[ul - 1]) * u; 
    seg_modify(ul, ur, -u, bit_q); 
    ans += bit_query(sze[u], bit_q); 
    ans += seg_query(ul, ur, bit_u) * u; 

    trav(u)
        if (v != fa[u])
        {
            int t_s = max_else(u, sze[v]); 
            int l = 2 * sze[v] - n, r = n - 2 * t_s; 
            
            seg_modify(l, r, u, bit_q); 
            bit_modify(sze[u], -1, bit_u); 
            bit_modify(n - sze[v], 1, bit_u); 

            dfs(v); 

            seg_modify(l, r, -u, bit_q);
            bit_modify(sze[u], 1, bit_u); 
            bit_modify(n - sze[v], -1, bit_u);  
        }

    seg_modify(ul, ur, u, bit_q); 
}

inline void solve()
{
    dfs_init(1); 

    for (int i = 1; i <= n; ++i)
        ++sum[sze[i]]; 
    for (int i = 1; i <= n; ++i)
        sum[i] += sum[i - 1]; 

    dfs(1); 

    putint(ans); 
    putchar('\n'); 
}

int main()
{
    freopen("centroid.in", "r", stdin); 
    freopen("centroid.out", "w", stdout); 

    int orzczk; 
    read(orzczk); 
    while (orzczk--)
    {
        init(); 
        solve(); 

    }
    return 0; 
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!