• 【cf741】D. Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths(dsu on tree)


    传送门

    题意:
    给出一颗以(1)为根的有根树,树边带有一个字符((a)~(v))的信息。
    输出对于每个结点,其子树内最长的简单路径并且满足边上的字符能够组成回文串。

    思路:

    • 显然最终的答案分为两部分,子树内部的答案,经过当前根结点的答案。
    • 第一种答案很好处理。类似于点分治,主要处理第二种答案。
    • 树上路径可以考虑找到(lca),维护点到根节点的信息。
    • 题目中的回文串可以等价于,出现奇数次的字符不超过(1)个。我们将字符状压一下,那么维护点到根的信息就很方便了;同理求出两点间的信息也很方便。
    • 因为要枚举两条链中的结点,我们可以类似于树(dp)那样,保留之前的信息,然后枚举这条链更新答案并且更新信息。
    • 这里显然枚举时枚举轻边最优(类似于启发式合并),那么可以采用(dsu on tree),算法会保留重儿子的信息,我们直接暴力轻儿子即可。

    根据(dsu on tree)算法的思想,每个结点只会被暴力到(O(logn))次,所以算法的时间复杂度为(O(nlogn))
    感觉挺考察对(dsu on tree)的理解的。。细节见代码吧:

    /*
     * Author:  heyuhhh
     * Created Time:  2019/11/15 16:10:20
     */
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #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 << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 5e5 + 5;
     
    int n;
    char s[N];
    vector <int> g[N];
    int dis[N], sz[N], bson[N], d[N];
    int f[1 << 22];
    int ans[N];
     
    void dfs(int u, int fa) {
        sz[u] = 1; 
        int mx = 0;
        d[u] = d[fa] + 1;
        if(fa) dis[u] = (dis[fa] ^ (1 << (s[u] - 'a')));
        for(auto v : g[u]) {
            dfs(v, u);
            sz[u] += sz[v];
            if(sz[v] > mx) {
                mx = sz[v]; bson[u] = v;
            }
        }
    }
     
    int Max, D;
     
    void upd(int u) {
        if(f[dis[u]]) Max = max(Max, f[dis[u]] + d[u] - D);
        for(int i = 0; i < 22; i++) if(f[dis[u] ^ (1 << i)]) {
            Max = max(Max, f[dis[u] ^ (1 << i)] + d[u] - D);
        }    
    }
     
    void upd2(int u) {
        f[dis[u]] = max(f[dis[u]], d[u]);
    }
     
    void go(int u) {
        upd(u);
        for(auto v : g[u]) go(v);
    }
     
    void go2(int u) {
        upd2(u);    
        for(auto v : g[u]) go2(v);   
    }
     
    void clear(int u) {
        f[dis[u]] = 0;
        for(auto v : g[u]) clear(v);   
    }
     
    void dfs2(int u, int fa, int op) {
        for(auto v : g[u]) if(v != bson[u]) dfs2(v, u, 0);
        if(bson[u]) dfs2(bson[u], u, 1);
        for(auto v : g[u]) Max = max(Max, ans[v]);
        D = 2 * d[u];
        for(auto v : g[u]) if(v != bson[u]) {
            go(v); go2(v); 
        }
        upd(u); upd2(u);
        ans[u] = Max;
        if(!op) {
            clear(u), Max = 0;
        }
    }
     
    void run(){
        for(int i = 2; i <= n; i++) {
            int p;
            scanf("%d %c", &p, &s[i]);
            g[p].push_back(i);
        }
        dfs(1, 0);
        dfs2(1, 0, 1);
        for(int i = 1; i <= n; i++) cout << ans[i] << " 
    "[i == n];
    }
     
    int main() {
        while(cin >> n) run();
    	return 0;
    }
    
  • 相关阅读:
    如何使用谷歌的自定义搜索引擎来搜寻一个ASP.NET网站
    [导入][FMS开发笔记]理解应用程序实例(聊天室房间的划分)
    WEB页面自打开的响应顺序
    Windows下SVN配置管理员指南
    [导入]Ajax基本过程
    [导入]FMS 中文帮助 (下载)
    [导入][Flash开发笔记] 系列
    [导入]mootools框架【三】Array篇: 方法完全解析
    [导入]mootools框架【七】Common篇: mootools的构造应用的基础设施类Common.js
    [导入]mootools框架【十】mootools深层探讨
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11868231.html
Copyright © 2020-2023  润新知