zoukankan      html  css  js  c++  java
  • 2021NUAA暑假集训 Day2 题解

    比赛链接:21.7.13-NUAA暑期集训
    比赛码:NUAAACM20210713



    A - 数的直径

    两遍(dfs),第一遍从根开始找到最深的结点,第二遍从最深的结点开始得到树的直径。

    #include <cstring>
    #include <iostream>
    using namespace std;
    
    struct Edge {
        int v, next;
    } edge[200010];
    int head[100010], cnt;
    int n, dis[100010], start;
    
    void addEdge(int u, int v) {
        edge[++cnt].v = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    void dfs(int x, int fa) {
        for (int i = head[x]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (v != fa) {
                dis[v] = dis[x] + 1;
                dfs(v, x);
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 1; i < n; ++i) {
            int x, y;
            cin >> x >> y;
            addEdge(x, y);
            addEdge(y, x);
        }
        memset(dis, 0x3f3f3f3f, sizeof(dis));
        dis[1] = 0;
        dfs(1, 0);
        int maxn = -1;
        for (int i = 1; i <= n; ++i) {
            if (dis[i] > maxn) {
                maxn = dis[i];
                start = i;  // 树的直径的起点
            }
        }
        memset(dis, 0x3f3f3f3f, sizeof(dis));
        dis[start] = 0;
        dfs(start, 0);
        maxn = -1;
        for (int i = 1; i <= n; ++i) {
            if (dis[i] > maxn) {
                maxn = dis[i];
            }
        }
        cout << maxn << endl;
        return 0;
    }
    

    B - Kay and Snowflake

    一个树的重心,只会在根本身或者最大的子树当中产生。
    先判断当前根能否作为重心,如果不能,就从最大的子树的重心往上面找。

    #include <iostream>
    #include <vector>
    using namespace std;
    
    int n, q, child[300010], Size[300010], res[300010], fa[300010];
    vector<int> g[300010];
    
    void dfs(int x) {
        Size[x] = 1;
        for (int i = 0; i < g[x].size(); ++i) {
            dfs(g[x][i]);
            Size[x] += Size[g[x][i]];
            if (Size[g[x][i]] > Size[child[x]]) {
                child[x] = g[x][i];
            }
        }
        if (Size[child[x]] * 2 > Size[x]) {
            int now = res[child[x]];
            while ((Size[x] - Size[now]) * 2 > Size[x]) {
                now = fa[now];
            }
            res[x] = now;
        } else {
            res[x] = x;
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n >> q;
        for (int i = 2, x; i <= n; i++) {
            cin >> x;
            fa[i] = x;
            g[x].push_back(i);
        }
        dfs(1);
        while (q--) {
            int x;
            cin >> x;
            cout << res[x] << endl;
        }
        return 0;
    }
    

    C - Fools and Roads

    用树上差分来统计,(u)走向(v),则(u)(v)之间的路径上的边权都加(1),用差分数组表示就是(diff[u]+1, diff[v]+1, diff[lca(u,v)]-2)
    注意以点代边的思想。

    #include <iostream>
    using namespace std;
    
    struct Edge {
        int v, next;
    } edge[200010];
    struct Line {
        int x, y;
    } line[100010];
    int cnt, head[100010], depth[100010], fa[100010][25], diff[100010], n, m, ans[100010], id[100010];
    
    void swap(int &x, int &y) {
        x ^= y;
        y ^= x;
        x ^= y;
    }
    
    void addEdge(int u, int v) {
        edge[++cnt].v = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    void dfs(int u, int pre) {
        depth[u] = depth[pre] + 1;
        fa[u][0] = pre;
        for (int i = 1; (1 << i) <= depth[u]; ++i) {
            fa[u][i] = fa[fa[u][i - 1]][i - 1];
        }
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (v != pre) {
                dfs(v, u);
            }
        }
    }
    
    int lca(int u, int v) {
        if (depth[u] > depth[v]) {
            swap(u, v);
        }
        for (int i = 20; i >= 0; --i) {
            if (depth[u] <= depth[v] - (1 << i)) {
                v = fa[v][i];
            }
        }
        if (u == v) {
            return u;
        }
        for (int i = 20; i >= 0; --i) {
            if (fa[u][i] != fa[v][i]) {
                u = fa[u][i], v = fa[v][i];
            }
        }
        return fa[u][0];
    }
    
    void sum(int u) {
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (v != fa[u][0]) {
                sum(v);
                diff[u] += diff[v];
            }
        }
        ans[id[u]] = diff[u];
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 1; i < n; ++i) {
            cin >> line[i].x >> line[i].y;
            addEdge(line[i].x, line[i].y);
            addEdge(line[i].y, line[i].x);
        }
        dfs(1, 0);
        for (int i = 1; i < n; ++i) {
            if (depth[line[i].x] > depth[line[i].y]) {
                id[line[i].x] = i;
            } else {
                id[line[i].y] = i;
            }
        }
        cin >> m;
        while (m--) {
            int x, y;
            cin >> x >> y;
            int Lca = lca(x, y);
            diff[x]++;
            diff[y]++;
            diff[Lca] -= 2;
        }
        sum(1);
        for (int i = 1; i < n; ++i) {
            cout << ans[i] << ' ';
        }
        return 0;
    }
    

    D - Apple Tree

    (dfs)序的两个数组(in)(out)记录某结点进入和出来的时间,则对于一个结点(u),其子树中某一结点(v)满足(in_u < in_v < out_v < out_u)
    用树状数组存储各时间戳所对应的结点的状况,则(getSum(out_u) - getSum(in_u - 1))所得结果就是以(u)为根的子树的状况。
    由于每次进入或者出来时间戳都会加一,所以树状数组的大小应为(2 imes n)

    #include <iostream>
    using namespace std;
    
    struct Edge {
        int v, next;
    } edge[100010];
    char s;
    int x;
    int in[100010], out[100010], sum[200010], tot;
    int n, m, cnt, head[100010];
    bool vis[100010];
    
    void addEdge(int u, int v) {
        edge[++cnt].v = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    int lowbit(int x) { return x & (-x); }
    
    void dfs(int x) {
        in[x] = ++tot;
        for (int i = head[x]; i; i = edge[i].next) {
            dfs(edge[i].v);
        }
        out[x] = ++tot;
    }
    
    void update(int x, int add) {  // 树状数组更新
        while (x <= 2 * n) {
            sum[x] += add;
            x += lowbit(x);
        }
    }
    
    int getSum(int x) {
        int ans = 0;
        while (x > 0) {
            ans += sum[x];
            x -= lowbit(x);
        }
        return ans;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 1; i < n; ++i) {
            int u, v;
            cin >> u >> v;
            addEdge(u, v);
        }
        dfs(1);
        for (int i = 1; i <= n; ++i) {
            update(in[i], 1);
            vis[i] = 1;
        }
        cin >> m;
        while (m--) {
            cin >> s >> x;
            if (s == 'C') {
                if (vis[x]) {
                    update(in[x], -1);
                } else {
                    update(in[x], 1);
                }
                vis[x] ^= 1;
            }
            if (s == 'Q') {
                cout << getSum(out[x]) - getSum(in[x] - 1) << endl;
            }
        }
        return 0;
    }
    

    E - Max Flow

    用树上差分来统计,点的差分与边的差分有所不同,对于(u)(v)之间的路径上的点权都加(1),用差分数组表示就是(diff[u]+1, diff[v]+1, diff[lca(u,v)]-1, diff[fa[lca(u,v)][0]]-1)

    #include <iostream>
    using namespace std;
    
    int head[50010], cnt;
    struct Edge {
        int v, next;
    } edge[100010];
    
    void addEdge(int u, int v) {
        edge[++cnt].v = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    int n, k, ans, fa[50010][25], depth[50010];
    int diff[50010];
    
    void dfs(int u, int pre) {
        depth[u] = depth[pre] + 1;
        for (int i = 1; (1 << i) <= depth[u]; ++i) {
            fa[u][i] = fa[fa[u][i - 1]][i - 1];
        }
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (v != pre) {
                fa[v][0] = u;
                dfs(v, u);
            }
        }
    }
    
    int lca(int u, int v) {
        if (depth[u] > depth[v]) {
            swap(u, v);
        }
        for (int i = 20; i >= 0; --i) {
            if (depth[u] <= depth[v] - (1 << i)) {
                v = fa[v][i];
            }
        }
        if (u == v) {
            return u;
        }
        for (int i = 20; i >= 0; --i) {
            if (fa[u][i] != fa[v][i]) {
                u = fa[u][i], v = fa[v][i];
            }
        }
        return fa[u][0];
    }
    
    void getSum(int u, int pre) {
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (v != pre) {
                getSum(v, u);
                diff[u] += diff[v];
            }
        }
        ans = max(ans, diff[u]);
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n >> k;
        for (int i = 1; i < n; ++i) {
            int u, v;
            cin >> u >> v;
            addEdge(u, v);
            addEdge(v, u);
        }
        dfs(1, 0);
        for (int i = 0; i < k; i++) {
            int u, v;
            cin >> u >> v;
            int Lca = lca(u, v);
            diff[u]++;
            diff[v]++;
            diff[Lca]--;
            diff[fa[Lca][0]]--;
        }
        getSum(1, 0);
        cout << ans << endl;
        return 0;
    }
    

    F - Count Descendants

    (dfs)序,记录每一个点进入的时间(in_i)和出来的时间(out_i),则对于一个结点(u),其子树中某一结点(v)满足(in_u < in_v < out_v < out_u)
    由此,用(vector)维护相同深度下所有点的(in)(dfs)时间戳是有序的,所以甚至不需要排序),然后每次二分查找(in_u)(out_u)的位置并相减即可。

    #include <iostream>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    struct Edge {
        int v, next;
    } edge[400010];
    int cnt, head[200010];
    int n, in[200010], out[200010], tot, depth[200010];
    int t;
    
    void addEdge(int u, int v) {
        edge[++cnt].v = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    vector<int> vec[200010];
    
    void dfs(int u) {
        in[u] = ++tot;
        vec[depth[u]].push_back(in[u]);
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            depth[v] = depth[u] + 1;
            dfs(v);
        }
        out[u] = ++tot;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n;
        for (int i = 2, x; i <= n; ++i) {
            cin >> x;
            addEdge(x, i);
        }
        dfs(1);
        cin >> t;
        while (t--) {
            int u, d;
            cin >> u >> d;
            cout << lower_bound(vec[d].begin(), vec[d].end(), out[u]) - lower_bound(vec[d].begin(), vec[d].end(), in[u]) << endl;
        }
        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 - The Tag Game

    (A)一直沿(A)(B)之间的最短路径走,(B)则往深度更大的结点走。
    所以求出刚开始(A)(B)之间的路径,找到此路径上(B)能在(A)之前到达且深度最大的结点(C),则(ans =( 1到C的距离+C与其子树中深度最大的叶子结点之间的距离)*2)

    #include <cstring>
    #include <iostream>
    using namespace std;
    
    int n, x, step, d[200010], path[200010], vis[200010], depth[200010];
    int cnt, head[200010];
    struct Edge {
        int v, next;
    } edge[400010];
    
    void addEdge(int u, int v) {
        edge[++cnt].v = v;
        edge[cnt].next = head[u];
        head[u] = cnt;
    }
    
    int dfs(int u, int fa) {  // 求深度
        int maxn = 0;
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (v != fa) {
                depth[v] = depth[u] + 1;
                maxn = max(dfs(v, u), maxn);
            }
        }
        return d[u] = maxn + 1;
    }
    
    void find_path(int u, int fa, int k) {
        if (step) {
            return;
        }
        path[k] = u;
        if (u == x) {
            step = k;
            return;
        }
        for (int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].v;
            if (v != fa) {
                find_path(v, u, k + 1);
            }
        }
    }
    
    int main() {
        while (cin >> n >> x) {
            cnt = 0;
            memset(head, 0, sizeof(head));
            for (int i = 1, u, v; i < n; ++i) {
                cin >> u >> v;
                addEdge(u, v);
                addEdge(v, u);
            }
            int ans = 0;
            memset(d, 0, sizeof(d));
            memset(vis, 0, sizeof(vis));
            step = 0, depth[1] = 1;
            dfs(1, 0);
            find_path(1, 0, 1);
            for (int i = step, j = 1; i && !vis[i]; --i, ++j) {
                vis[j] = 1;
                if (!vis[i]) {
                    ans = max(ans, d[path[i]] - 1 + depth[path[i]] - 1);
                }
            }
            cout << ans * 2 << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    docker容器的应用
    KVM虚拟机迁移
    centos6.5虚拟机快照技术
    centos6.5网络虚拟化技术
    centos6.5制作OpenStack云平台Windows7镜像
    centos6.5远程桌面连接(VNCSPice)
    centos6.5kvm虚拟化技术
    centos7安装Jenkins及其卸载(yum和rpm安装)
    CentOS 7安装JDK
    [leetcode]Reverse Nodes in k-Group
  • 原文地址:https://www.cnblogs.com/IzumiSagiri/p/15009085.html
Copyright © 2011-2022 走看看