zoukankan      html  css  js  c++  java
  • 【知识点】虚树

    简介:

    (听名字高大上,实际上没什么东西……虚树的题主要难在如何操作虚树)

    给出$k$个关键点,我们要建出一棵只包含这些关键点和他们$lca$的点数最少的树,以实现$dp$等操作。

    标志性的数据范围是$sum{k}leq 10^{5}$之类的。

    建树方法:

    1.将所有关键点按$dfs$序排序。

    2.开一个栈表示根到当前点的虚树路径,并把根丢进去。

    3.对于每一个关键点$u$:令$lca$为$u$与$s[top]$的最近公共祖先。

    若$lca=s[top]$,则把$u$丢进去。

    否则,我们需要弹出栈中所有不在根到$u$路径上的点。

    由于关键点是按照$dfs$序排序的,那么栈中所有$dep$大于$dep[lca]$的点都不在这条路径上。(容易验证它们一定在$lca$的另外一颗子树上)

    于是一直弹到最后一个不满足要求的点,每弹出一个点之后就在它和栈顶间连一条边。

    由于我们要把$lca$丢进栈里,所以最后一个点应当与$lca$连边,然后弹掉。

    最后把$lca$和$u$丢进栈里。(如果$s[top]=lca$就不用丢$lca$进去了)

    4.最后把栈里所有点弹掉,每弹出一个点之后就在它和栈顶间连一条边。

    复杂度分析:

    每加一个点最多产生一个$lca$,那么虚树中的总点数是$O(2k)$的。

    清空数组的时候千万要计算好复杂度。

    代码(CF613D Kingdom and its Cities):

    #include<bits/stdc++.h>
    #define maxn 200005
    #define maxm 500005
    #define inf 0x7fffffff
    #define ll long long
    #define debug(x) cerr<<#x<<": "<<x<<endl
    #define fgx cerr<<"--------------"<<endl
    #define dgx cerr<<"=============="<<endl
    
    using namespace std;
    int f[maxn][20],dep[maxn],dfn[maxn],tot;
    int hd[maxn],to[maxn<<1],nxt[maxn<<1],cnt;
    int st[maxn],po[maxn],vis[maxn],ans,flag; 
    vector<int> vc[maxn];
    
    inline int read(){
        int x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    inline bool cmp(int a,int b){return dfn[a]<dfn[b];}
    inline void add(int a,int b){vc[a].push_back(b);}
    
    inline void addedge(int u,int v){
        to[++cnt]=v,nxt[cnt]=hd[u],hd[u]=cnt;
        to[++cnt]=u,nxt[cnt]=hd[v],hd[v]=cnt;
    }
    
    inline void dfs(int u,int fa){
        dfn[u]=++tot,dep[u]=dep[fa]+1,f[u][0]=fa;
        for(int i=1;i<20;i++) f[u][i]=f[f[u][i-1]][i-1];
        for(int i=hd[u];i;i=nxt[i]) if(to[i]!=fa) dfs(to[i],u);
    }
    
    inline int lca(int u,int v){
        if(dep[u]<dep[v]) swap(u,v);
        for(int i=19;i>=0;i--)
            if(dep[f[u][i]]>=dep[v]) u=f[u][i];
        if(u==v) return u;
        for(int i=19;i>=0;i--)
            if(f[u][i]!=f[v][i]) 
                u=f[u][i],v=f[v][i];
        return f[u][0];
    }
    
    inline int solve(int u){
        int num=0;
        for(int i=0;i<vc[u].size();i++){
            int v=vc[u][i]; num+=solve(v);
            if(vis[v]&&vis[u]&&dep[v]==dep[u]+1) flag=1;
        }
        vc[u].clear();
        if(vis[u]){ans+=num;return 1;}
        else{
            if(num<=1) return num;
            else {ans++;return 0;}
        }
    }
    
    int main(){
        int n=read();
        for(int i=1;i<=n-1;i++)
            addedge(read(),read());
        dfs(1,0);
        int Q=read();
        while(Q--){
            int k=read(); ans=0,flag=0;
            for(int i=1;i<=k;i++) po[i]=read(),vis[po[i]]=1;
            sort(po+1,po+1+k,cmp);
            st[0]=0,st[++st[0]]=1;
            for(int i=1;i<=k;i++){
                if(st[0]==1){if(po[i]!=1)st[++st[0]]=po[i];continue;}
                int lt=lca(po[i],st[st[0]]);
                while(st[0]>1 && dep[lt]<dep[st[st[0]-1]])
                    add(st[st[0]-1],st[st[0]]),st[0]--;
                if(dep[lt]<dep[st[st[0]]]) add(lt,st[st[0]]),st[0]--;
                if(st[st[0]]!=lt) st[++st[0]]=lt; st[++st[0]]=po[i];
            }
            while(st[0]>1) add(st[st[0]-1],st[st[0]]),st[0]--;
            solve(1),printf("%d
    ",(flag)?-1:ans);
            for(int i=1;i<=k;i++) vis[po[i]]=0;
        }
        return 0;
    }
    虚树
  • 相关阅读:
    Python入门-函数进阶
    Python入门-初始函数
    Leetcode300. Longest Increasing Subsequence最长上升子序列
    Leetcode139. Word Break单词拆分
    Leetcode279. Perfect Squares完全平方数
    Leetcode319. Bulb Switcher灯泡开关
    Leetcode322. Coin Change零钱兑换
    二叉树三种遍历两种方法(递归和迭代)
    Leetcode145. Binary Tree Postorder Traversal二叉树的后序遍历
    Leetcode515. Find Largest Value in Each Tree Row在每个树行中找最大值
  • 原文地址:https://www.cnblogs.com/YSFAC/p/12037564.html
Copyright © 2011-2022 走看看