• 【bzoj3522/4543】[POI2014]Hotel加强版(长链剖分+dp)


    传送门

    神仙题。。简单版本很好做,做法也很多。
    加强版(nleq 10^5),显然之前的(O(n^2))的做法时间、空间复杂度都不能承受。
    考虑维护以深度有关的(dp):

    • (f[i][j])表示以(i)为根节点的子树中,深度为(j)的点有多少个。

    显然这个很好维护,转移(displaystyle f[i][j]=sum_{k}f[k][j-1]),我们可以用长链剖分加速。
    因为我们要枚举(3)个点,现在还需要一个(dp)维护另外两个点的信息。

    • (g[i][j])表示以(i)为根节点的子树中,点对((x,y))的个数有多少个,点对要满足(x,y,x ot ={y})(lca)的距离相等,并且从(lca)(i)这段距离为(d-j)。也就是说还需要一条长度为(j)的链进行匹配。

    考虑如何转移:

    • 显然可以直接从儿子进行转移,即(g[i][j]=g[k][j+1])
    • 从不同儿子子树中选取两个:(g[i][j]=f[k_1][j-1]*f[k_2][j-1])。此时两个结点的(lca)一定为(i)

    注意第一种转移跟深度有关系,但是和之前有点区别,此处我们还是可以通过长链剖分来进行优化;第二种转移可以直接进行枚举,这里枚举的深度会受到限制,总的枚举次数为(O(长链长度))

    之后考虑如何维护答案。
    显然最终的答案有两种情况:

    • 中心点为某一个结点,此时答案为(g[i][0])
    • 中心点不为某一个结点,此时答案为(f[i][j]*g[k][j+1]+f[k][j-1]*g[i][j])。主要就考虑了((2,1),(1,2))这两种情况,其实((1,1,1))这种也考虑了的,但已经被包含入((2,1))了。

    以上过程我们在一边枚举轻儿子时一边进行转移&统计答案。
    注意(g[i][0])要先加上,否则可能会重复统计。
    细节见代码:

    /*
     * Author:  heyuhhh
     * Created Time:  2020/6/10 23:23:27
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <assert.h>
    #include <functional>
    #include <numeric>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << std::endl; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
      template <template<typename...> class T, typename t, typename... A> 
      void err(const T <t> &arg, const A&... args) {
      for (auto &v : arg) std::cout << v << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e5 + 5;
    
    int n;
    vector <int> G[N];
    
    ll *f[N], *g[N], ans;
    ll tmp[N << 2], *id = tmp;
    
    int len[N], bson[N];
    void dfs(int u, int fa) {
        int Max = 0;
        for (auto v : G[u]) if (v != fa) {
            dfs(v, u);
            if (len[v] > Max) {
                Max = len[v];
                bson[u] = v;
            }
        }
        len[u] = len[bson[u]] + 1;
    }
    void dfs2(int u, int fa) {
        f[u][0] = 1;
        if (bson[u]) {
            //处理重链
            int v = bson[u];
            f[v] = f[u] + 1;
            g[v] = g[u] - 1;
            dfs2(v, u);
        }
        ans += g[u][0];
        for (auto v : G[u]) {
            if (v == fa || v == bson[u]) continue;
            //分配空间
            f[v] = id, id += (len[v] << 1);
            g[v] = id, id += (len[v] << 1);
            dfs2(v, u);
            //从轻链转移
            for (int i = 0; i < len[v]; i++) {
                ans += f[v][i] * g[u][i + 1];
                if (i) {
                    ans += f[u][i - 1] * g[v][i];
                }
            }
            for (int i = 1; i <= len[v]; i++) {
                if (i < len[v]) {
                    g[u][i - 1] += g[v][i];
                }
                g[u][i] += f[u][i] * f[v][i - 1];
                f[u][i] += f[v][i - 1];
            }
        }
    }
    void run() {
        cin >> n;
        for (int i = 1; i < n; i++) {
            int u, v; cin >> u >> v;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        dfs(1, 0);
        f[1] = id, id += (len[1] << 1);
        g[1] = id, id += (len[1] << 1);
        dfs2(1, 0);
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    
  • 相关阅读:
    交叉编译GDB
    交叉编译Python
    DEBUG memory问题的一些工具以及注意事项
    [qemu] QEMU虚拟机的安装步骤
    分享一个public domain 的大数运算库,可以用于RSA加密解密
    [KERNEL] 在procfs中新增文件的方法
    [backtrace]在不改变代码的情况下,打印可执行文件的backtrace
    【幻化万千戏红尘】qianfeng-JAVA课程结束总结
    【幻化万千戏红尘】qianfengDay30-正则表达式、注解注释基础学习:
    【幻化万千戏红尘】qianfengDay29-Json解析、Gson解析、FASTJSON解析基础学习:
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/13113392.html
Copyright © 2020-2023  润新知