虚树
虚树
有这样一类问题:给出一棵\(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;
}

浙公网安备 33010602011771号