卡着时间过得,大概是因为全用了ll,时间涨了一倍吧??

懒得改了,第一道虚树还是思路比较重要

下面这段文字是复制来的:

给出一棵树.

每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可以通过某种方式剔除而不影响最终结果.

于是就有了建虚树这个技巧.....

我们可以用log级别的时间求出点对间的lca....

那么,对于每个询问我们根据原树的信息重新建树,这棵树中要尽量少地包含未选择节点. 这棵树就叫做虚树.

接下来所说的"树"均指虚树,原来那棵树叫做"原树".

构建过程如下:

按照原树的dfs序号(记为dfn)递增顺序遍历选择的节点. 每次遍历节点都把这个节点插到树上.

首先虚树一定要有一个根. 随便扯一个不会成为询问点的点作根.

维护一个栈,它表示在我们已经(用之前的那些点)构建完毕的虚树上,以最后一个插入的点为端点的DFS链.

设最后插入的点为p(就是栈顶的点),当前遍历到的点为x.我们想把x插入到我们已经构建的树上去.

求出lca(p,x),记为lca.有两种情况:

  1.p和x分立在lca的两棵子树下.

  2.lca是p.

  (为什么lca不能是x?

   因为如果lca是x,说明dfn(lca)=dfn(x)<dfn(a),而我们是按照dfs序号遍历的,于是dfn(a)<dfn(x),矛盾.)

对于第二种情况,直接在栈中插入节点x即可,不要连接任何边(后面会说为什么).

对于第一种情况,要仔细分析.

我们是按照dfs序号遍历的(因为很重要所以多说几遍......),有dfn(x)>dfn(p)>dfn(lca).

这说明什么呢? 说明一件很重要的事:我们已经把lca所引领的子树中,p所在的子树全部遍历完了!

  简略的证明:如果没有遍历完,那么肯定有一个未加入的点h,满足dfn(h)<dfn(x),

        我们按照dfs序号递增顺序遍历的话,应该把h加进来了才能考虑x.

这样,我们就直接构建lca引领的,p所在的那个子树. 我们在退栈的时候构建子树.

p所在的子树如果还有其它部分,它一定在之前就构建好了(所有退栈的点都已经被正确地连入树中了),就剩那条链.

如何正确地把p到lca那部分连进去呢?

设栈顶的节点为p,栈顶第二个节点为q.

重复以下操作:

  如果dfn(q)>dfn(lca),可以直接连边q->p,然后退一次栈.

  如果dfn(q)=dfn(lca),说明q=lca,直接连边lca->p,此时子树已经构建完毕.

  如果dfn(q)<dfn(lca),说明lca被p与q夹在中间,此时连边lca->q,退一次栈,再把lca压入栈.此时子树构建完毕.

    如果不理解这样操作的缘由可以画画图.....

最后,为了维护dfs链,要把x压入栈. 整个过程就是这样.....

题解:

对于这一道题目,朴素的树形dp是nm的

而容易发现,某些路径中的点是没有用的,所以考虑建立一颗虚树

两条边之间的权值就是实际权值的最小值

当然这题会发现有个性质就是如果某个点在另一个点的子树中,这个点是不用考虑的(事实上这个优化对效率并没有什么卵用)

至于那个dp,没什么难度,就看代码吧

代码:

#include <bits/stdc++.h>
using namespace std;
#define maxn 1000000
#define maxn2 300000
#define INF 1e15
#define ll long long
struct re{
ll a,b,c;
}a[maxn],a2[maxn];
ll gg,m,n,l,l1,number[maxn],dep[maxn],head[maxn],
head2[maxn],ql[maxn],bz1[maxn2][],bz2[maxn2][],
nowtime[maxn],now,f[maxn],b[maxn],k,st[maxn],top;
void dfs(ll x,ll fa)
{
number[x]=++now;
bz1[x][]=fa;
ll u=head[x];
while (u)
{
ll v=a[u].b;
if (v!=fa)
{
bz2[v][]=a[u].c; dep[v]=dep[x]+;
dfs(v,x);
}
u=a[u].a;
}
}
bool cmp(ll x,ll y)
{
return(number[x]<number[y]);
}
ll lca(ll x,ll y)
{
if (dep[x]<dep[y]) swap(x,y);
for (ll i=;i>=;i--)
if (dep[x]-(<<i)>=dep[y]) x=bz1[x][i];
if (x==y) return(x);
for (ll i=;i>=;i--)
if (bz1[x][i]!=bz1[y][i])
x=bz1[x][i],y=bz1[y][i];
return(bz1[x][]);
}
ll query(ll x,ll y)
{
if (dep[x]<dep[y]) swap(x,y);
ll ans=INF;
for (ll i=;i>=;i--)
if (dep[x]-(<<i)>=dep[y])
ans=min(ans,bz2[x][i]),x=bz1[x][i];
if (x==y) return(ans);
for (ll i=;i>=;i--)
if (bz1[x][i]!=bz1[y][i])
{
ans=min(ans,bz2[x][i]);
ans=min(ans,bz2[y][i]);
x=bz1[x][i],y=bz1[y][i];
}
ans=min(ans,bz2[x][]);
ans=min(ans,bz2[y][]);
return(ans);
}
void dp(ll x,ll fa)
{
ll u=head2[x],tmp=;
while (u)
{
ll v=a2[u].b;
if (v!=fa)
{
f[v]=a2[u].c;
dp(v,x);
tmp+=f[v];
}
u=a2[u].a;
}
if (nowtime[x]!=gg) f[x]=min(f[x],tmp);
}
void arr(ll x,ll y,ll z)
{
a[++l1].a=head[x];
a[l1].b=y;
a[l1].c=z;
head[x]=l1;
}
void arr2(ll x,ll y)
{
if (x==y) return;
a2[++l].a=head2[x];
ql[l]=x;
a2[l].b=y;
a2[l].c=query(x,y);
head2[x]=l;
}
deque <ll> q;
void solve()
{
for (ll i=;i<=l;i++) head2[ql[i]]=;l=;//清理head的一种不太浪费的方法
q.clear();
q.push_back(b[]);
for (ll i=;i<=k;i++)
//if (lca(b[i],b[q.back()])!=q.back())
q.push_back(b[i]); //按照上面说的如果在别人子树中就被丢弃
top=;
st[++top]=;
while (!q.empty())
{
ll now=q.front(),tmp=lca(st[top],now);
q.pop_front();
while (true)
{
if (dep[tmp]>=dep[st[top-]])
{
arr2(tmp,st[top]);
arr2(st[top],tmp); top--;
if (st[top]!=tmp) st[++top]=tmp;
break;
}
arr2(st[top-],st[top]);
arr2(st[top],st[top-]); top--;
}
//if (st[top]!=now) //这句是为了防止有相同点 这题里可以不需要
st[++top]=now;
}
while (top>)
{
arr2(st[top],st[top-]);
arr2(st[top-],st[top]),top--;
}
f[]=INF;dp(,);
cout<<f[]<<endl;
}
int main()
{
std::ios::sync_with_stdio(false);
cin>>n;
ll c,d,e;
for (ll i=;i<=n-;i++)
{
cin>>c>>d>>e;
arr(c,d,e); arr(d,c,e);
}
for (ll i=;i<=;i++)
for (ll j=;j<=maxn2-;j++) bz2[j][i]=INF;
dfs(,);
for (ll i=;i<=;i++)
for (ll j=;j<=n;j++)
{
bz1[j][i]=bz1[bz1[j][i-]][i-];
bz2[j][i]=min(bz2[j][i-],bz2[bz1[j][i-]][i-]);
}
cin>>m;
for (gg=;gg<=m;gg++)
{
cin>>k;
for (ll i=;i<=k;i++)
{
cin>>b[i];
nowtime[b[i]]=gg;
}
sort(b+,b++k,cmp);
solve();
}
}
    top=;
st[++top]=;
while (!q.empty())
{
ll now=q.front(),tmp=lca(st[top],now);
q.pop_front();
while (true)
{
if (dep[tmp]>=dep[st[top-]])
{
arr2(tmp,st[top]);
arr2(st[top],tmp); top--;
if (st[top]!=tmp) st[++top]=tmp;
break;
}
arr2(st[top-],st[top]);
arr2(st[top],st[top-]); top--;
}
if (st[top]!=now) st[++top]=now;
}

while (top>1)
      {
         arr2(st[top],st[top-1]);
         arr2(st[top-1],st[top]),top--;
      }

 

真正和虚树有关的就上面这么一点,基本背板子就可以了

自己归纳的虚树步骤

首先找一个不可能出现的点作为第一个节点

然后对于新加入的点,求出其与st[top]的lca

当lca>=st[top-1]的时候 就连边st[top-1]------->lca 结束此次循环

当lca<st[top-1]的时候 九离岸边st[top-1]------>st[top] 然后递归一下

做完后判断新加入的点是否存在,如果不存在就加入到栈中

最后再将剩余的连完

这个步骤的原理上面已经说了 感觉理解也是挺直观的

虚树------sdoi2011<消耗战>的更多相关文章

  1. 【学习笔记/题解】虚树/[SDOI2011]消耗战

    题目戳我 \(\text{Solution:}\) 题目很显然可以设\(dp[i]\)表示\(i\)的子树内的关键点都不和\(i\)联通的最小待机,有如下\(dp\)方程: \(v\in son_u, ...

  2. [模板] 虚树 && bzoj2286-[Sdoi2011]消耗战

    简介 虚树可以解决一些关于树上一部分节点的问题. 对于一棵树 \(T\) 的一个子集 \(S\), 可以在 \(O(|S| \log |S|)\) 的时间复杂度内求出 \(S\) 的虚树. 虚树包括根 ...

  3. 算法复习——虚树(消耗战bzoj2286)

    题目: Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战 ...

  4. bzoj 2286: [Sdoi2011]消耗战 虚树+树dp

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description 在一 ...

  5. [BZOJ2286][SDOI2011]消耗战(虚树DP)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4998  Solved: 1867[Submit][Statu ...

  6. BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 4261  Solved: 1552 [Submit][Sta ...

  7. 【BZOJ2286】[Sdoi2011]消耗战 虚树

    [BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的 ...

  8. bzoj2286 (sdoi2011)消耗战(虚树)

    [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4052  Solved: 1463[Submit][Status][Dis ...

  9. [Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4896  Solved: 1824[Submit][Statu ...

随机推荐

  1. Python排序算法之快速排序

    快速排序(quickSort) 快排的思想:首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序. 百度百 ...

  2. String类型的方法总结

    String :字符串对象的包装类型 var stringObject = new String("wanglehui"); 方法总结: 1.返回该对象表示的基本字符串值(也就是返 ...

  3. Java——Struts2 crud 简单实例(学习struts2和ssh) 用Myeclipse实现

    1.new web project 2.给新建的web项目添加struts2支持 3.项目结构中有了struts.xml和struts2核心库 4.编码 4.1项目结构图 4.2源代码: (1)DbU ...

  4. sizeof strlen区别于联系

    http://www.cnblogs.com/carekee/articles/1630789.html

  5. CSS :invalid 选择器

    如果 input 元素中的值是非法的,实时提醒 <!DOCTYPE html> <html> <head> <meta charset="utf-8 ...

  6. android 解决子线程进行UI操作

    Android确实不允许在子线程中进行UI操作的,但我们有时必须在子线程里去执行一些耗时的任务,然后根据任务的执行结果来更新相应的UI控件. Android提供了一套异步消息处理机制,可以解决子线程中 ...

  7. 2017-2018-2 20155303『网络对抗技术』Exp9:Web安全基础

    2017-2018-2 『网络对抗技术』Exp9:Web安全基础 --------CONTENTS-------- 一.基础问题回答 1.SQL注入攻击原理,如何防御? 2.XSS攻击的原理,如何防御 ...

  8. weblogic中部署SSH项目遇到的坑

    总结将SSH项目部署到weblogic遇到的坑.项目中是SSH,另外还用到了webservice.quartz等框架.在tomcat部署是可以的,现在总结部署到weblogic遇到的坑. 在这里说一下 ...

  9. pycharm2018破解

    1.下载 链接:https://pan.baidu.com/s/1G0C9xoUQg6JRgNQYLMIi1w 密码:2z3x 2.修改 "G:\Python\JetBrains\PyCha ...

  10. 基于Apache的阿里云部署Node.js服务器(Windows环境)

    1 前言 由于nodejs项目对方开放了多个端口,而且阿里云上的Apache服务器(windows)已经挂载了网站,此时需要把此项目也挂上去,网上查询资料,方法略少,基本是基于nginx版本的. 2  ...