首页 > 其他分享 >【题解】P4565 [CTSC2018]暴力写挂

【题解】P4565 [CTSC2018]暴力写挂

时间:2023-01-14 19:02:56浏览次数:49  
标签:sz P4565 int 题解 son fa CTSC2018 edge res

能写点分为什么要写这种玄学东西。

思路

边分树合并。

首先考虑点分,发现只会 T 飞的做法。但是答案的形式有点意思,换一下写法:

\(ans = \frac{1}{2} \max(\operatorname{dis}(x, y) + \operatorname{depth}(x) + \operatorname{depth}(y) + \operatorname{depth^{\prime}}(\operatorname{lca^{\prime}}(x, y)))\)

有树上距离了,好办。\(\operatorname{dis}(x, y)\) 可以看成边分时的 \(x, y\) 深度之和,于是可以把它和 \(\operatorname{depth}(x), \operatorname{depth}(y)\)分别拆分给 \(x, y\),等价于给 \(x, y\) 赋上点权 \(val\)

正常来说这里和点分一样建一棵 \(T^{\prime}\) 的虚树,然后枚举每个 \(\operatorname{lca^{\prime}}\) 的贡献就做完了。但是这样的时间复杂度是 \(O(n \log^2 n)\),被无良出题人卡掉了。

可以倒过来想,先枚举 \(\operatorname{lca^{\prime}}\),再考虑它的子树中一对被同一层边分的结点 \(x, y\) 的贡献。

考虑一对结点 \(x, y\) 第一次产生贡献的条件。当它们最后一次被一起边分之后,它们会处在两层不同的边分中。

那么根据边分树的性质,它们在边分树上的路径有一段公共前缀,然后分别走进父结点的左右子结点中。

只维护子结点构成的路径时,可以考虑遍历一遍子结点的边分树,然后和当前的答案合并。

然而我们维护的是子树,所以需要将边分树不断向上合并,可以用类似线段树合并的方法实现。

维护答案也不用上面那么麻烦,直接在合并的时候一起算就行。

时间复杂度 \(O(n \log n)\),三度化和线段树合并有一点常数。

代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;

typedef long long ll;

const int maxn = 4e5 + 5;
const int nd_sz = 1e7 + 5;
const int maxe = 2e6 + 5;
const ll inf = 1e18;

int n, gpt, gb;
int rt[maxn];
int son[nd_sz][2];
ll ans = -inf, res = -inf;
ll val[nd_sz];
vector<int> g[maxn], w[maxn];

namespace t1
{
    struct Edge
    {
        int to, nxt, w;
        bool vis;
    } edge[maxe];

    int ecnt = 1, rte, tot, min_sz;
    int head[nd_sz], rdp[nd_sz], sz[nd_sz];
    ll dep[nd_sz], pre[nd_sz];

    void add_edge(int u, int v, int w)
    {
        edge[++ecnt] = (Edge){v, head[u], w};
        head[u] = ecnt;
    }

    void get_rdp(int u, int fa)
    {
        for (int i = head[u]; i; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if (v == fa) continue;
            rdp[v] = rdp[u] + 1;
            dep[v] = dep[u] + edge[i].w;
            get_rdp(v, u);
        }
    }

    void cnt_sz(int u, int fa)
    {
        tot++;
        for (int i = head[u]; i; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if (edge[i].vis || (v == fa)) continue;
            // if (v == 4) printf("debug %d\n", i);
            cnt_sz(v, u);
        }
    }

    void get_sz(int u, int fa)
    {
        sz[u] = 1;
        for (int i = head[u]; i; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if (edge[i].vis || (v == fa)) continue;
            get_sz(v, u);
            sz[u] += sz[v];
            int tsz = abs(tot - 2 * sz[v]);
            if (tsz < min_sz) min_sz = tsz, rte = i;
        }
    }

    void get_pre(int u, int fa, int dir)
    {
        if (u <= n)
        {
            int cur = gb++;
            val[cur] = dep[u] + pre[u];
            // printf("son[%d][%d] = %d\n", rt[u], dir, cur);
            son[rt[u]][dir] = cur;
            rt[u] = cur;
        }
        for (int i = head[u]; i; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if (edge[i].vis || (v == fa)) continue;
            pre[v] = pre[u] + edge[i].w;
            get_pre(v, u, dir);
        }
    }
    
    void solve(int u)
    {
        tot = 0;
        cnt_sz(u, 0);
        if (tot == 1) return;
        // printf("%d\n", tot);
        min_sz = 1e9;
        get_sz(u, 0);
        int a = edge[rte].to, b = edge[rte ^ 1].to;
        // printf("%d <-> %d\n", edge[4].vis, edge[4 ^ 1].vis);
        // printf("solving %d, %d, %d %d : %d -> %d\n", u, rte, a, b, rte, rte);
        edge[rte].vis = edge[rte ^ 1].vis = true;
        pre[a] = edge[rte].w, pre[b] = 0;
        get_pre(a, 0, rdp[a] > rdp[b]);
        get_pre(b, 0, rdp[b] > rdp[a]);
        solve(a), solve(b);
    }
}

int merge(int u, int v)
{
    if (!(u && v)) return u | v;
    if (son[u][0] && son[v][1]) res = max(res, val[son[u][0]] + val[son[v][1]]);
    if (son[v][0] && son[u][1]) res = max(res, val[son[v][0]] + val[son[u][1]]);
    // printf("debug %lld\n", res);
    val[u] = max(val[u], val[v]);
    son[u][0] = merge(son[u][0], son[v][0]);
    son[u][1] = merge(son[u][1], son[v][1]);
    return u;
}

namespace t2
{
    struct Edge
    {
        int to, nxt, w;
    } edge[maxe];

    int ecnt = 0;
    int head[maxn];
    ll dep[maxn];

    void add_edge(int u, int v, int w)
    {
        edge[++ecnt] = (Edge){v, head[u], w};
        head[u] = ecnt;
    }

    void dfs(int u, int fa)
    {
        for (int i = head[u]; i; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if (v == fa) continue;
            dep[v] = dep[u] + edge[i].w;
            dfs(v, u);
            // printf("%d -> %d\n", u, dep[v]);
            res = -inf;
            rt[u] = merge(rt[u], rt[v]);
            // printf("%lld\n", res);
            if (res != -inf) ans = max(ans, res - 2 * dep[u]);
        }
    }
}

void add_edge(int u, int v, int d) { g[u].push_back(v); w[u].push_back(d); }

void rebuild(int u, int fa)
{
    int lst = u;
    for (int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i], d = w[u][i];
        if (v == fa) continue;
        int cur = gpt++;
        t1::add_edge(lst, cur, 0);
        t1::add_edge(cur, lst, 0);
        t1::add_edge(cur, v, d);
        t1::add_edge(v, cur, d);
        rebuild(v, u);
        lst = cur;
    }
}

int main()
{
    scanf("%d", &n);
    gpt = n + 1, gb = n + 1;
    for (int i = 1; i <= n; i++) rt[i] = i;
    val[0] = -inf;
    for (int i = 1, u, v, d; i <= n - 1; i++)
    {
        scanf("%d%d%d", &u, &v, &d);
        add_edge(u, v, d);
        add_edge(v, u, d);
    }
    for (int i = 1, u, v, d; i <= n - 1; i++)
    {
        scanf("%d%d%d", &u, &v, &d);
        t2::add_edge(u, v, d);
        t2::add_edge(v, u, d);
    }
    rebuild(1, 0);
    t1::get_rdp(1, 0);
    t1::solve(1);
    // puts("done");
    for (int i = 1; i <= n; i++) rt[i] = i;
    t2::dfs(1, 0);
    ans /= 2;
    for (int i = 1; i <= n; i++) ans = max(ans, t1::dep[i] - t2::dep[i]);
    printf("%lld\n", ans);
    return 0;
}

/*
6
1 2 2
1 3 0
2 4 1
2 5 -7
3 6 0
1 2 -1
2 3 -1
2 5 3
2 6 -2
3 4 8

5
*/

标签:sz,P4565,int,题解,son,fa,CTSC2018,edge,res
From: https://www.cnblogs.com/lingspace/p/p4565.html

相关文章

  • Codeforces 1630 E Making It Bipartite 题解 (Dilworth定理)
    题目链接首先可以想到把题目中的那张图G建出来,由于要求这张图是二分图,把它复制一遍(\(G\toG'\)),然后对于每个u,连一条无向边\(u-u'\),这样就变成了最大独立集问题。但是一......
  • Codeforces 1630 E Making It Bipartite 题解 (Dilworth定理)
    题目链接首先可以想到把题目中的那张图G建出来,由于要求这张图是二分图,把它复制一遍(\(G\toG'\)),然后对于每个u,连一条无向边\(u-u'\),这样就变成了最大独立集问题。但是一......
  • P1390 公约数的和 题解
    传送门题意:求出\(\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\gcd(i,j)\)原式\(=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i-1}\gcd(i,j)\)\(=\sum\limits_{d=1......
  • P4220 题解
    前言题目传送门!更好的阅读体验?思路代码为了使代码更容易通过,可以像我一样膜拜大佬,获得随机种子,通过的概率更大。#include<iostream>#include<cstdio>#include<......
  • 【题解】P5030 长脖子鹿放置(网络流)
    长脖子鹿放置题目背景众周所知,在西洋棋中,我们有城堡、骑士、皇后、主教和长脖子鹿。题目描述如图所示,西洋棋的“长脖子鹿”,类似于中国象棋的马,但按照“目”字攻击,且没......
  • 1.14模拟赛题解
    T1考虑枚举线段的中点,计算它对答案的贡献。时间复杂度\(O(nm)\)。T2首先可以计算出最大流量\(maxf=\dfrac{sum}{len}\)。那么就可以将\(k\)条路径当成一条来看。把......
  • 【题解】P4292 [WC2010]重建计划
    【乐正绫AI】世末歌者「Cotton」绫绫,有你AI的每一天,我都很幸福[大笑][大笑][大笑]【乐正绫AI】世末歌者【砖厂浪人&TsingClouds】绫绫,有你的每一天,我都很幸福[大笑][大......
  • Centos7下安装Dogtail GUI自动化测试工具并打开sniff工具过程中遇到的问题解决方法
    (目录)因为测试需要,需在Centos下进行liunxGUI软件自动化测试,所以用到了python的Dogtail库,继而使用Dogtail的sniff控件获取工具,但是遇到了很多问题记录如下。1环境Cent......
  • SPOJ LCMSUM 题解
    LCMSUM题意:求:\(\sum\limits_{i=1}^n\lim(i,n)\)数据范围:\(1\leqT\leq3\times10^5\),\(1\leqn\leq10^6\)。原式\(=\sum\limits_{i=1}^n\frac{i\timesn}{\gc......
  • 题解 CF678D【Iterated Linear Function】
    暴力解法。初学群论,可能写的不是很严谨,望大佬指正。problem\[g^{(n)}(x)=\begin{cases}x,&(n=0).\\f(g^{(n-1)}),&(n>0).\end{cases}\]其中\(f\)是一次函数。给出......