【知识点】虚树

简介:

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

给出$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\n",(flag)?-1:ans);
        for(int i=1;i<=k;i++) vis[po[i]]=0;
    }
    return 0;
}
虚树

 

posted @ 2019-12-13 22:17  Fugtemypt  阅读(323)  评论(0编辑  收藏  举报