zoukankan      html  css  js  c++  java
  • Codeforces 1528C

    Description

    思路

    方法1
    在树1上dfs,可以确保访问的点是树链,满足性质1;假设把访问到的点都在树2上标记,并将标记点之间的路径缩短到只有一条边,这样就构成一颗新树。而满足性质2的最大个数就是新树的叶子结点个数。只要我们能维护好叶子结点的个数,其中最大值就是答案。

    使用树剖,就可以动态查询插入或删除结点是否增加或减少了叶子结点个数。

    时间复杂度:(O(nlog^2{n}))

    #include <bits/stdc++.h>
     
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
     
    using namespace std;
    /*-----------------------------------------------------------------*/
     
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
     
    const int N = 5e5 + 10;
    const double eps = 1e-5;
     
    int son[N], siz[N], h[N], nxt[N], dep[N], p[N], fa[N], top[N], cnt, rnk[N], dfn[N], si;
    ll sum[N];
    int n;
    int ans;
    bool vis[N];
    int tarr[N];
    int lowbit(int x) {
        return x&-x;
    }
    int get(int p) {
        int res = 0;
        while(p) {
            res += tarr[p];
            p -= lowbit(p); 
        }
        return res;
    }
     
    void Add(int p, int val) {
        while(p <= n) {
            tarr[p] += val;
            p += lowbit(p);
        }
    }
     
    void addval(int l, int r, int val) {
        Add(l, val);
        Add(r + 1, -val);
    } 
     
    void dfs1(int o) {
    son[o] = -1;
    siz[o] = 1;
    for (int j = h[o]; j; j = nxt[j])
      if (!dep[p[j]]) {
        dep[p[j]] = dep[o] + 1;
        sum[p[j]] = sum[o] + 1;
        fa[p[j]] = o;
        dfs1(p[j]);
        siz[o] += siz[p[j]];
        if (son[o] == -1 || siz[p[j]] > siz[son[o]]) son[o] = p[j];
      }
    }
     
    void dfs2(int o, int t) {
    top[o] = t;
    cnt++;
    dfn[o] = cnt;
    rnk[cnt] = o;
    if (son[o] == -1) return;
    dfs2(son[o], t);  // 优先对重儿子进行 DFS,可以保证同一条重链上的点 DFS 序连续
    for (int j = h[o]; j; j = nxt[j])
      if (p[j] != son[o] && p[j] != fa[o]) dfs2(p[j], p[j]);
    }
     
    void add(int u, int v) {
      si++;
      nxt[si] = h[u];
      h[u] = si;
      p[si] = v;
    }
     
    void upd(int p, int val) {
        while(p) {
            int l = dfn[top[p]], r = dfn[p];
            addval(l, r, val);
            p = fa[top[p]];
        }
    }
     
    int getid(int p) {
        while(p) {
            int l = dfn[top[p]], r = dfn[p];
            if(get(l)) {
                while(l <= r) {
                    int mid = (l + r) / 2;
                    if(get(mid)) l = mid + 1;
                    else r = mid - 1;
                }
                p = rnk[r];
                break;
            }
            p = fa[top[p]];
        }
        return p;
    }
     
    int que(int p) {
        return get(dfn[p]);
    }
     
    vector<int> np[N];
     
     
    void solve(int p, int fa, int num) {
        bool flag = false;
        if(!que(p)) {
            int tar = getid(p);
            if(!tar || (que(tar) - vis[tar] >= 1)) flag = true;
        }
        upd(p, 1);
        vis[p] = 1;
        ans = max(ans, num + flag);
        for(int nt : np[p]) {
            if(nt == fa) continue;
            solve(nt, p, num + flag);
        }
        upd(p, -1);
        vis[p] = 0;
    }
     
    int main() {
        IOS;
        int t;
        cin >> t;
        while(t--) {
            cin >> n;
            cnt = 0;
            si = 0;
            for(int i = 1; i <= n; i++) {
                np[i].clear();
                dep[i] = 0;
                vis[i] = 0;
                tarr[i] = 0;
                h[i] = 0;
            }
            for(int i = 2; i <= n; i++) {
                int f;
                cin >> f;
                np[i].push_back(f);
                np[f].push_back(i);
            }
            for(int i = 2; i <= n; i++) {
                int f;
                cin >> f;
                add(f, i);
            }
            dfs1(1);
            dfs2(1, 1);
            ans = 0;
            solve(1, 0, 0);
            cout << ans << endl;
        }
    }
    

    方法2
    官方题解的方法确实巧妙。
    从方法1知道,最大个数就是叶子结点个数。假设当前维护了一个叶子结点集(S),新加入的结点为(v)
    只有(forall uin S, u既不是v的祖先也不在v的子树中),v才可以被加入到(S)中。若(v)(u)的子树中,用(v)替换(u)一定是最优的,所以加点策略如下:

    1. (v)(u)的祖先,忽略(v)
    2. (v)(u)的子树中,用(v)替换(u)
    3. 否则,将(v)加入(S)中。

    如何判断(v)(u)的关系,可以在预处理一个(st_i)(ft_i),分别代表dfs过程中第一次访问(i)点的时间和最后一次访问的时间,那么

    1. (v)(u)的祖先,(st_vle st_u, ft_v ge ft_u)
    2. (v)(u)的子树中,(st_vge st_u, ft_v le ft_u)

    对于第一种情况,将({st[u], u})插入集合。判断时,找到第一个(保证和v最近)(st[u] ge st[v]),再check (ft[u]le ft[v]),如果是,说明(v)(u)的祖先。

    第二种情况同理。

    #include <bits/stdc++.h>
     
    #define endl '
    '
    #define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
    #define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
    #define FI freopen(".//data_generator//in.txt","r",stdin)
    #define FO freopen("res.txt","w",stdout)
    #define mp make_pair
    #define seteps(N) fixed << setprecision(N) 
    typedef long long ll;
     
    using namespace std;
    /*-----------------------------------------------------------------*/
     
    ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
    #define INF 0x3f3f3f3f
     
    const int N = 5e5 + 10;
    const double eps = 1e-5;
    typedef pair<int, int> PII;
    int st[N], ft[N];
    vector<int> np1[N];
    vector<int> np2[N];
    set<PII> ps;
    int ans;
    int ti;
    void dfs1(int p, int f) {
        st[p] = ++ti;
        for(int nt : np2[p]) {
            if(nt == f) continue;
            dfs1(nt, p);
        }
        ft[p] = ++ti;
    }
     
    void solve(int p, int f) {
        bool ise = false;
        PII keep;
        auto tar = ps.lower_bound({st[p], 0});
        if(tar == ps.end() || ft[p] <= ft[tar->second]) {
            tar = ps.upper_bound({st[p], 0});
            if(tar != ps.begin()) {
                tar--;
                int pre = tar->second;
                if(ft[p] <= ft[tar->second]) {
                    keep = *tar;
                    ps.erase(tar);
                    ise = true;
                }
            }
            ps.insert({st[p], p});
        }
        ans = max(ans, (int)ps.size());
        for(int nt : np1[p]) {
            if(nt == f) continue;
            solve(nt, p);
        }
        if(ps.count({st[p], p})) ps.erase({st[p], p});
        if(ise) ps.insert(keep);
    }
     
    int main() {
        IOS;
        int t;
        cin >> t;
        while(t--) {
            int n;
            cin >> n;
            ps.clear();
            for(int i =1 ; i<= n; i++) {
                np1[i].clear();
                np2[i].clear();
     
            }
            for(int i = 2; i <= n; i++) {
                int f;
                cin >> f;
                np1[i].push_back(f);
                np1[f].push_back(i);
            }
            for(int i = 2; i <= n; i++) {
                int f;
                cin >> f;
                np2[i].push_back(f);
                np2[f].push_back(i);
            }
            ti = 0;
            ans = 0;
            dfs1(1, 0);
            solve(1, 0);
            cout << ans << endl;
        }
    }
    
  • 相关阅读:
    C#中IPAddress转换成整型int
    没有注册类 (异常来自 HRESULT:0x80040154 (REGDB_E_CLASSNOTREG))
    VB.NET或C#报错:You must hava a license to use this ActiveX control.
    c#几种随机数组和数组乱序
    C#封装的websocket协议类
    VB生成条形码(EAN-13)
    VB控件间的拖放
    VB用API模拟截屏键PrintScreen
    VB读写进程的内存
    几个VB常见又内涵的错误
  • 原文地址:https://www.cnblogs.com/limil/p/14826445.html
Copyright © 2011-2022 走看看