虚树

虚树

有这样一类问题:给出一棵\(n\)个结点的树(\(n\)\(10^5\)级别),每次指定\(m\)个结点,给予它们一些性质,求出某个答案,保证\(\sum m\)\(n\)同阶。

从保证总和这一点就可以看出,每次询问的复杂度必须基于\(m\),这样才能保证总复杂度。但是这类问题往往需要通过树形DP来解决,这样复杂度就是基于\(n\)的,超时。

由此我们可以利用单调栈建出一棵“虚树”,它保证了每次求解只会在一棵节点数与\(m\)同阶的树上进行,且建树过程只需要\(O(m·logn)\)的时间。


建树:

大体的思想是我们先把所有结点按DFS序排序,然后依次插入。

每当我们枚举到结点\(x\),求出它与当前栈顶结点的LCA结点\(w\),然后不断弹出栈顶结点并连边,直到\(w\)存在当前栈顶结点的子树中,这样我们保证了任何时间栈中的所有结点都在一条链上,然后再加入结点\(x\)

具体实现仍然要注意很多细节,建树的常数也经常成为程序速度的瓶颈,但是有一些DP是可以在出栈时直接统计答案而不需要建树的,要多加注意。

一个更加具体的例子:http://www.voidcn.com/blog/a915800048/article/p-5963403.html

rep(i,1,m)
 {
  scanf("%d",&p);
  rep(j,1,p) scanf("%d",&h[j]),vis[h[j]]=1;
  
  sort(h+1,h+p+1,cmp);

  Esize=0,size=1,st[1]=1,mark=1;
  ans1=0,ans2=INF,ans3=-INF;

  rep(j,1,p)
  {
   if (h[j]==h[j-1]) continue;
   
   w=query(h[j],st[size]);
   
   while ((L[w]<L[st[size]])&&(R[w]>R[st[size]]))
   {
    if ((L[w]>=L[st[size-1]])&&(R[st[size-1]]>=R[w]))
    {
     addpdge(w,st[size],depth[st[size]]-depth[w]),size--;
     break;
    }
    addpdge(st[size-1],st[size],depth[st[size]]-depth[st[size-1]]);
    size--;
   }
   
   if (st[size]!=w) st[++size]=w;
   st[++size]=h[j];

   if ((j>1)&&(w==1)) mark=0;
  }
posted @ 2016-12-26 20:45  Krew  阅读(532)  评论(0)    收藏  举报