BZOJ4543 [POI2014]Hotel加强版

Address

Solution

  • 三个点两两距离相等,相当于选出三条长度相等且只有唯一公共点的路径。
  • f [ x ] [ i ] f[x][i] f[x][i] 表示点 x x x 的子树内与点 x x x 距离为 i i i 的点数,转移显然为: f [ x ] [ i ] = ∑ y ∈ c h i l d r e n [ x ] f [ y ] [ i − 1 ] f[x][i] =\sum\limits_{y \in children[x]} f[y][i - 1] f[x][i]=ychildren[x]f[y][i1]
  • g [ x ] [ i ] g[x][i] g[x][i] 表示在点 x x x 的子树内选取两点,满足两点到其 L C A LCA LCA 的距离为 d d d L C A LCA LCA 到点 x x x 的距离为 d − i d - i di 的方案数,转移只要额外考虑 L C A LCA LCA 为点 x x x d = i d = i d=i)的情况,其余情况可以由子节点的 g g g 得到,即: g [ x ] [ i ] = ∑ y ∈ c h i l d r e n [ x ] ( g [ y ] [ i + 1 ] + f ′ [ x ] [ i ] × f [ y ] [ i − 1 ] ) ( f ′ , g ′ 表 示 处 理 y 这 棵 子 树 前 f 的 值 ) g[x][i] = \sum \limits_{y \in children[x]}(g[y][i + 1] + f'[x][i] \times f[y][i - 1])(f',g'表示处理 y这棵子树前 f 的值) g[x][i]=ychildren[x](g[y][i+1]+f[x][i]×f[y][i1])f,gyf
  • 则答案 a n s = ∑ x = 1 n ∑ y ∈ c h i l d r e n [ x ] ∑ i ( f ′ [ x ] i ] × g [ y ] [ i + 1 ] + g ′ [ x ] [ i ] × f [ y ] [ i − 1 ] ) ans = \sum \limits_{x = 1}^{n}\sum \limits_{y \in children[x]}\sum \limits_{i}(f'[x]i] \times g[y][i + 1] + g'[x][i] \times f[y][i - 1]) ans=x=1nychildren[x]i(f[x]i]×g[y][i+1]+g[x][i]×f[y][i1])
  • 时间复杂度 O ( n 2 ) O(n^2) O(n2)
  • 考虑用类似 d s u   o n   t r e e dsu\ on\ tree dsu on tree 的思想优化,保存长链上子节点的信息(相当于将子节点的 f f f 右移, g g g 左移),其余节点暴力计算,时间复杂度相当于树中所有长链的长度之和,即为 O ( n ) O(n) O(n)
  • 空间问题用指针分配即可,具体参考实现。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
 
template <class T>
inline void read(T &res)
{
    char ch; 
    while (ch = getchar(), !isdigit(ch));
    res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
        res = res * 10 + ch - 48;
}
 
typedef long long ll;
const int N = 1e5 + 5;
const int M = 1e6 + 5;
 
int n, mx[N], son[N];
ll ans, tmp[M], *f[N], *g[N], *now = tmp + N;
 
struct Edge 
{
    int to; Edge *nxt;
}p[N << 1], *lst[N], *P = p;
 
 
inline void Link(int x, int y)
{
    (++P)->nxt = lst[x]; lst[x] = P; P->to = y;
    (++P)->nxt = lst[y]; lst[y] = P; P->to = x; 
}
 
inline void Create(int x)
{
    f[x] = now;
    now += mx[x] << 1 | 1;
    g[x] = now;
    now += mx[x] << 1 | 1;
}
 
inline void Dfs1(int x, int Fa)
{
    for (Edge *e = lst[x]; e; e = e->nxt)
    {
        int y = e->to;
        if (y == Fa)    
            continue;
        Dfs1(y, x);
         
        if (mx[y] + 1 > mx[x])
        {
            mx[x] = mx[y] + 1;
            son[x] = y; 
        }
    }
}
 
inline void Dfs2(int x, int Fa)
{
    if (son[x])
    {
        f[son[x]] = f[x] + 1;
        g[son[x]] = g[x] - 1;
        Dfs2(son[x], x);
    }
    f[x][0] = 1;
    ans += g[x][0];
    for (Edge *e = lst[x]; e; e = e->nxt)
    {
        int y = e->to;
        if (y == son[x] || y == Fa)
            continue;
        Create(y);
        Dfs2(y, x);
        for (int i = 0; i <= mx[y]; ++i)
        {
            ans += g[x][i + 1] * f[y][i];
            if (i)
                ans += f[x][i - 1] * g[y][i];
        }
        for (int i = 0; i <= mx[y]; ++i)
        {
            g[x][i + 1] += f[y][i] * f[x][i + 1];
            f[x][i + 1] += f[y][i];
            if (i) 
                g[x][i - 1] += g[y][i];
        }
    }
}
 
int main()
{
    read(n);
    for (int i = 1, x, y; i < n; ++i)
    {
        read(x); read(y);
        Link(x, y);
    }
     
    Dfs1(1, 0);
    Create(1);
    Dfs2(1, 0);
    std::cout << ans << std::endl;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值