「2020-2021 集训队作业」大鱼治水

「2020-2021 集训队作业」大鱼治水

Part 1

60次操作。想到重链剖分。

初始钦定选择每条重边,对于每一个到根的路径上,如果是重边就不管,否则先用两次操作变成合法的,再扭回去。

这样子就是对于点到根的路径上每一条重边需要0次操作,轻边需要4次操作。

怎么想?

  • 注意到对于每个轻边,我们扭过来在扭过去一共需要4次操作,也就是需要大概 $4 \log N$ 次操作,注意到刚好等于60.

  • 注意到 $q \leq 500000$ 所以时间复杂度大概是一个 $O(q \log n)$ 便可以联想到树剖

Part2

40次操作。

感觉一下上面的做法,对于重儿子很“重”的情况自然是很优的。但是如果重儿子不是那么重,那么就一点也不优。

考虑到设 $f_i$ 表示以 $i$ 为根的子树的答案(这里只需满足到 $i$ 的路径上都是染色边即可)。我们重新定义重儿子,定义重儿子为节点 $u$ 的儿子中 $f$ 值最大的儿子。对于节点 $u$ 我们假设对于它的所有儿子的 $f$ 值有 $f_i \geq f_{i+1}$。按照 Part1 的方法,设 $m$ 为 $u$ 的子节点数量,即 $f_u = \max\{f_1 ,\max \limits_{j = 2} \limits^{j \leq m} f_j + 4\}$

但是考虑到我们也可以直接暴力做这个东西,即每次操作先钦点选边,然后取消这个钦定,则 $f_u = \max\{f_1 + 2 ,\max \limits_{j = 2} \limits^{j \leq m} f_j + 2\}$

比较上面两种方案,我们可以得到每个点的具体策略,本地打表算一下最多40次操作。亲测59pts。

Part 3

35次操作。

再一次细化平衡策略!注意到轻边和重边需要的额外操作数量总和为4.于是考虑是不是有可能重边1次操作,轻边3次操作。考虑对于每一次重边,钦定它,用一次操作。对于每一个轻边,取消钦点,钦定它,然后取消,3次操作。

则 $f_u = \max\{f_1 + 1 ,\max \limits_{j = 2} \limits^{j \leq m} f_j + 3\}$

本地打表算一下,35次操作,可过。

// Problem: #3390. 「2020-2021 集训队作业」大鱼治水
// Contest: LibreOJ
// URL: https://loj.ac/p/3390
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
// #include <vector>
#include"river.h"

namespace my{
    const long long inf = 1e18;
    const int mininf = 1e9 + 7;
    #define pb push_back
    const int MAX = 5e4 + 10;

    std::vector <int> g[MAX];
    int f[MAX];
    int siz[MAX], son[MAX], dep[MAX];
    int Fa[MAX];
    int top[MAX], dfn[MAX], op[MAX], dfx[MAX];
    int clk;
    bool f2[MAX];

    void dfs(int u, int fa){
        dep[u] = dep[fa] + 1;
        Fa[u] = fa;
        siz[u] = 1;
        int maxn = 0;
        for(int i = 0; i < g[u].size(); i++){
            int v = g[u][i];
            if(v == fa) continue;
            dfs(v, u);
            if(f[v] >= f[maxn] or maxn == 0)    maxn = v;
        }
        son[u] = maxn;
        if(!son[u]){
            op[u] = 0;
            return ;
        }
        int j = son[u];
        int cl3 = 0;
        for(int i = 0; i < g[u].size(); i++){
            int v = g[u][i];
            if(v == Fa[u] or v == j)    continue;   
            f[u] = std::max(f[u], f[v] + 4);
            cl3 = std::max(cl3, f[v] + 3);
        }
        cl3 = std::max(cl3, f[j] + 1);
        f[u] = std::max(f[u], f[j]);
        if(cl3 < f[u] and cl3 < f[j] + 2){
            op[u] = 3;
            f[u] = cl3;
            f2[u] = 0;
            son[u] = maxn;
        }
        else if(f[u] > f[j] + 2){
            f[u] = f[j] + 2;
            op[u] = 2;
            son[u] = 0;
        }else{
            op[u] = 1;
        }
    }

    void dfs2(int u, int topu){
        dfn[u] = ++clk;
        dfx[clk] = u;
        top[u] = topu;
        int pre2;
        if(op[u] == 3){
            pre2 = son[u];
            son[u] = 0;
        }
        if(son[u] and op[u] != 3)   dfs2(son[u], topu);
        for(int i = 0; i < g[u].size(); i++){
            int v = g[u][i];
            if(v == Fa[u] or v == son[u])   continue;
            dfs2(v, v);
        }
        if(op[u] == 3){
            son[u] = pre2;
        }
    }

    std::vector<int> init(int n, std::vector<int> father){
        for(int i = 2; i <= n; i++){
            g[father[i - 2]].pb(i);
        }
        dfs(1, 1);
        dfs2(1, 1);
        std::vector <int> re;
        // std::re.clear();
        for(int i = 1; i <= n; i++){
            if(op[i] == 2 or op[i] == 3)    re.pb(0ll);
            else re.pb(son[i]);
        }
        return re;
    }
    void solve(int x){
        int pre2 = x;
        while(x != 1 and top[x] != 1){
            x = top[x];
            int pre = x;
            x = Fa[x];
            if(op[x] == 3){
                if(pre == son[x] and !f2[x]){
                    set(x, pre);
                    f2[x] = 1;
                }else if(pre != son[x]){
                    if(f2[x])   set(x, 0), f2[x] = 0;
                    set(x, pre);
                }
            }
            else if(op[x] == 1){
                if(pre == son[x]){

                    continue;
                }
                set(x, 0);
                set(x, pre);
            }else{
                set(x, pre);
            }
        }
        wait();
        x = pre2;
        while(x != 1 and top[x] != 1){
            x = top[x];
            int pre = x;
            x = Fa[x];
            if(op[x] == 3){
                if(pre == son[x]){

                }else{
                    set(x, 0);
                    f2[x] = 0;
                }
            }
            else if(op[x] == 1){
                if(pre == son[x])   continue;
                set(x, 0);
                set(x, son[x]);
            }else{
                set(x, 0);
            }
            x = top[x];
        }
        return ;
    }
}

std::vector<int> init(int n, std::vector<int> fa) {
    return my::init(n, fa);
}
void solve(int x){
    my::solve(x);
}

Part4?

注意到轻边和重边需要的额外操作数量总和为4,所以好像不是很有可能优化下去了?

总而言之,这是一道做了和做了一样的题。

posted @ 2023-12-08 18:32  WRuperD  阅读(5)  评论(0编辑  收藏  举报  来源

本文作者:DIVMonster

本文链接:https://www.cnblogs.com/guangzan/p/12886111.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

这是一条自定义内容

这是一条自定义内容