题目大意:

给一棵树,每次给出一些关键点,对于树上每个点,被离它最近的关键点(距离相同被标号最小的)控制

求每个关键点控制多少个点

分析:

虚树+dp

dp过程如下:

第一次dp,递归求出每个点子树中关键点到它距离最小值

第二次dp,用第一次的信息,从上往下转移,求出每个点到所有关键点中到它距离最小值

这里兼容性讨论一下,发现可以不用存次大值,因为若最小值来自要更新的子树,则子树中点到上面的点的距离一定不优

前两次dp求出了虚树中1,2类点被谁控制

第三次dp,对于每条边,找到断点,细节见代码

注意:

虚树中这样算会算漏很多原树中的点

如2-1-3树,根是1,关键点1,2,这样3会算漏

用tmp存1的sz,遍历虚树子树时把tmp减掉已经算过的,剩下的就属于1的控制

吐槽题目有毒,去行末空格就PE,%……&%……&@¥%&

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cctype>
using namespace std;
typedef long long LL;
const int M=300007;
const int INF=1e9;
inline int rd(){
    int x=0;bool f=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-')f=0;
    for(;isdigit(c);c=getchar())x=x*10+c-48;
    return x;
}

int n,m;

int g[M],hd[M],te,td;
struct edge{int y,next;}e[M<<1],dw[M];

int top[M],dfn[M],pid[M],tdfn;
int pre[M],dep[M],sz[M],son[M];

struct node{
    int x,id;
    node(int xx=0,int ii=0){x=xx;id=ii;}
}que[M];
bool operator <(node x,node y){
    if(x.x<y.x) return 1;
    if(x.x==y.x&&x.id<y.id) return 1;
    return 0;
}
int st[M],tot;

int ans[M];
int bl[M];
int bid[M];
int pbid[M];
node mn[M];//不求次大值,兼容性的问题

node gmn(node x,node y){
    return x<y?x:y;
}

bool cmp(node x,node y){return dfn[x.x]<dfn[y.x];}

void addedge(int x,int y){
    e[++te].y=y;e[te].next=g[x];g[x]=te;
}
void addlink(int x,int y){
    if(x==y) return;
    dw[++td].y=y;dw[td].next=hd[x];hd[x]=td;
}

void dfs1(int x){
    sz[x]=1;
    int p,y;
    for(p=g[x];p;p=e[p].next)
    if((y=e[p].y)!=pre[x]){
        pre[y]=x;
        dep[y]=dep[x]+1;
        dfs1(y);
        sz[x]+=sz[y];
        if(sz[y]>sz[son[x]]) son[x]=y;
    }
}

void dfs2(int x){
    pid[dfn[x]=++tdfn]=x;
    int p,y;
    if(son[x]){
        top[son[x]]=top[x];
        dfs2(son[x]);
    }
    for(p=g[x];p;p=e[p].next)
    if((y=e[p].y)!=pre[x]&&y!=son[x]){
        top[y]=y;
        dfs2(y);
    }
}

int LCA(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=pre[top[x]];
    }
    if(dep[x]>dep[y]) swap(x,y);
    return x;
}

int jumpc(int x,int y){
    while(dep[top[x]]>dep[y]){
        x=top[x];
        if(pre[x]==y) return x;
        x=pre[x];
    }
    return pid[dfn[y]+1];
}

int stp(int x,int y){
    int tp;
    while( y && (tp=dep[x]-dep[pre[top[x]]])<=y){//y&&
        y-=tp;
        x=pre[top[x]];
    }
    return pid[dfn[x]-y];
}

void vbuild(int z){
    sort(que+1,que+z+1,cmp);
    int i,x,anc;
    for(i=1;i<z;i++){
        x=LCA(que[i].x,que[i+1].x);
        hd[x]=0;
        bl[x]=0;
    }
    hd[1]=0;
    bl[1]=0;
    for(i=1;i<=z;i++){
        x=que[i].x;
        hd[x]=0;
        bl[x]=que[i].id;
        ans[que[i].id]=0;
    }

    td=tot=0;
    st[++tot]=1;
    for(i=1;i<=z;i++){
        x=que[i].x;
        anc=LCA(x,st[tot]);
        if(anc==st[tot]) st[++tot]=x;
        else{
            while(tot>1 && dep[anc]<=dep[st[tot-1]]){
                addlink(st[tot-1],st[tot]);
                tot--;
            }
            addlink(anc,st[tot]);
            st[tot]=anc;
            st[++tot]=x;
        }
    }
    for(i=1;i<tot;i++) addlink(st[i],st[i+1]);
}

void dp1(int x){
    if(!bl[x])mn[x]=node(INF,0);
    else mn[x]=node(0,pbid[bl[x]]);
    int p,y;
    node tp;
    for(p=hd[x];p;p=dw[p].next){
        y=dw[p].y;
        dp1(y);
        tp=mn[y];
        tp.x+=dep[y]-dep[x];
        mn[x]=min(mn[x],tp);
    }
}

void dp2(int x){
    if(!bl[x]) bl[x]=bid[mn[x].id];
    int p,y;
    node tp;
    for(p=hd[x];p;p=dw[p].next){
        y=dw[p].y;
        tp=mn[x];
        tp.x+=dep[y]-dep[x];
        mn[y]=min(mn[y],tp);
        dp2(y);
    }
}

void dp3(int x){
    int p,y,z,d,tp;
    int ss=sz[x];
    for(p=hd[x];p;p=dw[p].next){
        y=dw[p].y;
        d=mn[x].x+mn[y].x+dep[y]-dep[x]-1;//加上mn,最后要减1,算出要真正要竞争领地的两点间点数,点数!
        z=jumpc(y,x);
        ss-=sz[z];
        dp3(y);
        if(bl[x]==bl[y]) ans[bl[x]]+=sz[z]-sz[y];
        else{
            if(d%2==0){
                tp=stp(y,d/2-mn[y].x);
            }
            else{
                if(pbid[bl[y]]<pbid[bl[x]]) tp=stp(y,d/2+1-mn[y].x);//
                else tp=stp(y,d/2-mn[y].x);
            }
            ans[bl[x]]+=sz[z]-sz[tp];
            ans[bl[y]]+=sz[tp]-sz[y];
        }
    }
    ans[bl[x]]+=ss;//算漏的补上
}

int main(){
    freopen("a.txt","r",stdin);
    int i,x,y,z;
    n=rd();
    for(i=1;i<n;i++){
        x=rd(),y=rd();
        addedge(x,y);
        addedge(y,x);
    }

    dep[1]=pre[1]=0;
    dfs1(1);
    top[1]=1;
    dfs2(1);

    m=rd();
    while(m--){
        z=rd();
        for(i=1;i<=z;i++){
            que[i].x=rd();
            bid[que[i].x]=i;
            que[i].id=i;
            pbid[i]=que[i].x;
        }
        vbuild(z);
        dp1(1);
        dp2(1);
        dp3(1);
        for(i=1;i<=z;i++) printf("%d ",ans[i]);
        puts("");
    }
    return 0;
}

bzoj 3572世界树 虚树+dp的更多相关文章

  1. BZOJ 3572 世界树(虚树)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3572 思路:建立虚树,然后可以发现,每条边不是同归属于一端,那就是切开,一半给上面,一半给下面. # ...

  2. BZOJ 3572 [HNOI2014]世界树 (虚树+DP)

    题面:BZOJ传送门 洛谷传送门 题目大意:略 细节贼多的虚树$DP$ 先考虑只有一次询问的情况 一个节点$x$可能被它子树内的一个到x距离最小的特殊点管辖,还可能被管辖fa[x]的特殊点管辖 跑两次 ...

  3. BZOJ 3572: [Hnoi2014]世界树 [虚树 DP 倍增]

    传送门 题意: 一棵树,多次询问,给出$m$个点,求有几个点到给定点最近 写了一晚上... 当然要建虚树了,但是怎么$DP$啊 大爷题解传送门 我们先求出到虚树上某个点最近的关键点 然后枚举所有的边$ ...

  4. bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增

    [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1921  Solved: 1019[Submit][Status][Dis ...

  5. 洛谷 P3233 [HNOI2014]世界树(虚树+dp)

    题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...

  6. [HNOI2014][bzoj3572] 世界树 [虚树+dp]

    题面: 传送门 思路: 一道虚树的好题,是很多虚树博客的入门题目 但是我认为这道题目出的难点和亮点不在于虚树,而在于建出虚树以后dp的思路与实现 下文中为方便描述,用势力范围来表示一个“议事处”管辖的 ...

  7. bzoj 3572 [Hnoi2014]世界树——虚树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3572 关于虚树:https://www.cnblogs.com/zzqsblog/p/556 ...

  8. 【BZOJ】3572: [Hnoi2014]世界树 虚树+倍增

    [题意]给定n个点的树,m次询问,每次给定ki个特殊点,一个点会被最近的特殊点控制,询问每个特殊点控制多少点.n,m,Σki<=300000. [算法]虚树+倍增 [题解]★参考:thy_asd ...

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

    题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...

随机推荐

  1. 尝试在数据库 5 中提取逻辑页 (1:1640) 失败。该逻辑页属于分配单元XXX ,而非 XXX。

    此信息表明数据库或表 已经部分损坏可以通过以下步骤尝试修复: 1. DBCC CHECKDB 重启服务器后,在没有进行任何操作的情况下,在SQL查询分析器中执行以下SQL进行数据库的修复,修复数据库存 ...

  2. android studio没有浮现函数用法和属性说明?

    最近转用android studio,在使用eclipse和android studio时原本在鼠标停留处或智能提示能浮现文档相关内容,但我的是一直显示Fetching Documentation…… ...

  3. Android Camera Api的心得

    (一) 前言最近看Camera的api,觉得写的真的不错.现在翻译过来,给大家分享分享,译文可能不太好,大家将就着看哈. (二) 正文1. CameraCamera是Android framework ...

  4. 打开一个activity,让edittext不获得焦点

      在实际开发中,有的页面用到Edittext控件,这时候进入该页面可能会自动弹出输入法,这么显示不太友好,所以需要设置一下让Edittext默认不自动获取焦点.在网上查资料解决办法如下: 在Edit ...

  5. 关于tomcat启动没有进行编译或者编译报错的问题

    关于tomcat 的问题 如果项目没有编译 解决方案:1: 把项目刷新一下 然后Clean一下,之后等待右下角编译完成100%2: 有可能tomcat conf 里的配置文件的错误 进入查看下3: 如 ...

  6. HDU 1217 Arbitrage(Floyd的应用)

    给出一些国家之间的汇率,看看能否从中发现某些肮脏的......朋友交易. 这是Floyd的应用,dp思想,每次都选取最大值,最后看看自己跟自己的.....交易是否大于一.... #include< ...

  7. c# 添加了按钮双击事件后,再删除掉代码会提示错误

    有两种方法:.清空属性窗口中的双击事件(doubleclick )右边的内容: .单击“发生错误”提示窗口的“否”后,再双击错误列表里的错误项,此时编辑窗口跳转为xx.Designer.cs,然后注释 ...

  8. StartUML 各种类图的例子

    1.UML分为: 1)静态建模:系统基础和系统固定框架结构,这些图形往往是“静态”的. 类图(Class Diagram):常用来分析业务概念 用例图(Use Case Diagram):常用 对象图 ...

  9. CultureInfo中重要的InvariantCulture

    CultureInfo简述 CultureInfo类位于System.Globalization命名空间内,这个类和这个命名空间许多人都不了解也认为不需要太多了解,实际上,你写的程序中会经常间接得使用 ...

  10. Delphi的指针(转)

    源:http://blog.csdn.net/henreash/article/details/7368088 Pointers are like jumps, leading wildly from ...