JZOJ 4639 Angel Beats!【NOIP2016提高组A组7.16】

Angel Beats!

(这是一部日漫,7.16的出题人好神奇,名字都来自于影音作品)

题目大意

给你一棵1为根的树,然后会有q个询问,向你查询点x子树和点y子树的重心,重心可能会有很多个,你只需要输出距离和即可。

两棵子树的重心的定义如下:在树上找到一个点,使得该点到两棵子树中所有点距离之和最小,即这两棵子树的重心。

输入格式

第一行一个整数 ,代表点的数量。

接下来 n-1行,第i 行的表示节点i 的父亲节点。

接下来一个整数q ,为询问的个数。

接下来q 行,每行两个数x,y ,表示查询子树x 和子树y 的重心,输出这个点到两颗子树中所有点距离之和。

输出格式

输出有q行,每行一个整数,表示你求得的距离。

样例输入

9

1

2

3

3

7

2

7

1

3

3 7

3 2

7 9

样例输出

10

10

5

样例解释

数据范围

题解

首先预处理出以每个点为根节点时对应的子树大小,以及所有的点到每个点的距离和。

(方法很简单,把整棵树遍历一次即可,做法略,请读者自行思考)

我们同时也要预处理出以每个点为根节点对应的子树的重心。

先给出重心的定义和性质。

定义:以这个点为根,那么所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

性质:树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。

至于找重心,可以这样做。

假设我们要找以点v为根节点的子树的重心,那么它的重心一定在点o最大的子树的重心到点o的路径上(显然,证明略,请读者自行思考),我们可以用倍增算法去找,同时,我们找出来的点深度越大越好。(很显然,这个点的深度越大,这棵子树里的所有点到此点的距离和越小,证明略,请读者自行思考)

对于一个在以y为根的子树内的点x, 子树里的所有点到点x的距离和可以这么求,我们正难则反:

子树所有点到x距离和

=所有点到x距离和-非子树所有点到x距离和

=所有点到x距离和-非子树所有点到子树根距离和-(n-子树大小)× dist(x, y)

=所有点到x距离和-(所有点到子树根距离和-子树所有点到子树根距离和)-(n-子树大小)× (x的深度-y的深度)

我们发现上面的每一项我们都可以On预处理出来。

要做对这道题,我们还需要一些结论:

结论 1:两棵子树的重心一定在两棵子树各自重心连线的路径上。

证明:显然……

结论 2:在两棵子树size较大那棵内部一定能找到两棵子树重心。

证明:如果在树根连线上,一定能调整,越靠近Size较大的子树距离和越小,所以重心在那棵Size较大的子树内部,其中Size指子树的大小。(点数的多少)

我们一样用倍增算法在Size较大的子树的重心到其根节点的路径中找两棵子树的重心,深度一样是越深越好。

求出重心后,用预处理出来的信息经过一系列运算后便可算出答案(计算过程略,请读者自行思考)。

还有一点,算出答案我们还需要求出对于每个询问中的x与y的路径所经过的边数,一样用倍增求就可以了。

好吧,还有一点,如果点y在点x为根的子树内,或点x在点y为根的子树内,这种情况分开讨论即可。

Code(Pascal)

label 123;
const
    jx=18;
var
    n,i,j,k,o,l,p,x,y,jgd,cqy,kkk,op,dis,ans,q,zd,zgdx:longint;
    bz:array[0..101000,0..jx] of longint;
    fa,size,zx,en,sd,jd,kd:array[0..101000] of longint;
    bj:array[0..101000,1..2] of longint;
procedure qsort(l,r:longint);
    var
        i,j,m:longint;
    begin
        i:=l;
        j:=r;
        m:=bj[(l+r) div 2,1];
        repeat
            while bj[i,1]<m do inc(i);
            while bj[j,1]>m do dec(j);
            if i<=j then
            begin
                bj[0]:=bj[i];
                bj[i]:=bj[j];
                bj[j]:=bj[0];
                inc(i);
                dec(j);
            end;
        until i>j;
        if l<j then qsort(l,j);
        if i<r then qsort(i,r);
    end;
procedure dg(O:longint);
    var
        l,i:longint;
    begin
        l:=0;
        bz[o,l]:=fa[o];
        while bz[bz[o,l],l]>0 do
        begin
            bz[o,l+1]:=bz[bz[o,l],l];
            inc(l);
        end;
        for i:=en[o-1]+1 to en[o] do
        begin
            sd[bj[i,2]]:=sd[o]+1;
            dg(bj[i,2]);
            jd[o]:=jd[bj[i,2]]+jd[o]+size[bj[i,2]];
            size[o]:=size[bj[i,2]]+size[o];
        end;
        inc(size[o]);
    end;
procedure bl(o:longint);
    var
        i:longint;
    begin
        for i:=en[o-1]+1 to en[o] do
        begin
            kd[bj[i,2]]:=kd[o]-size[bj[i,2]]+(n-size[bj[i,2]]);
            bl(bj[i,2]);
        end;
    end;
procedure zzx(o:longint);
    var
        i,ms,u,uuu:longint;
    begin
        ms:=0;
        for i:=en[o-1]+1 to en[o] do
        begin
            zzx(bj[i,2]);
            if size[bj[i,2]]>size[ms] then ms:=bj[i,2];
        end;
        if o=1 then
        o:=1;
        if ms=0 then
        begin
            zx[o]:=o;
            exit;
        end;
        if size[ms]<=size[o]-size[ms] then zx[o]:=o
        else
        begin
            u:=zx[ms];
            l:=jx;
            uuu:=size[o];
            while l>=0 do
            begin
                if (sd[bz[u,l]]>=sd[o]) and (uuu-size[bz[u,l]]>uuu div 2) then
                u:=bz[u,l];
                dec(l);
            end;
            while uuu-size[u]>uuu div 2 do u:=fa[u];
            zx[o]:=u;
        end;
    end;
function xz(a,b:longint):longint;
    var
        i,j,k:longint;
    begin
        k:=jx;
        if sd[a]>sd[b] then
        while k>=0 do
        begin
            if sd[bz[a,k]]>=sd[b] then a:=bz[a,k];
            dec(k);
        end
        else
        while k>=0 do
        begin
            if sd[bz[b,k]]>=sd[a] then b:=bz[b,k];
            dec(k);
        end;
        k:=jx;
        while k>=0 do
        begin
            if sd[bz[a,k]]<>sd[bz[b,k]] then
            begin
                a:=bz[a,k];
                b:=bz[b,k];
            end;
            dec(k);
        end;
        while a<>b do
        begin
            a:=fa[a];
            b:=fa[b];
        end;
        exit(a);
    end;
begin
    readln(n);
    size[0]:=maxlongint;
    for i:=2 to n do
    begin
        readln(fa[i]);
        inc(en[fa[i]]);
        bj[i-1,1]:=fa[i];
        bj[i-1,2]:=i;
    end;
    for i:=1 to n do
    en[i]:=en[i-1]+en[i];
    sd[1]:=1;
    qsort(1,n-1);
    dg(1);
    kd[1]:=jd[1];
    bl(1);
    size[0]:=0;
    zzx(1);
    size[0]:=maxlongint;
    readln(q);
    for i:=1 to q do
    begin
        readln(x,y);
        zd:=xz(x,y);
        if (zd=x) or (zd=y) then
        begin
            cqy:=zx[zd];
            op:=zd;
            dis:=sd[cqy]-sd[op];
            ans:=kd[cqy]-(kd[op]-jd[op])-(n-size[op])*dis;
            goto 123;
        end;
        zd:=sd[zd];
        jgd:=sd[x]-zd+sd[y]-zd-1;
        if size[x]>=size[y] then
        begin
            op:=x;
            cqy:=zx[x];
        end
        else
        begin
            op:=y;
            cqy:=zx[y];
        end;
        kkk:=jx;
        zgdx:=size[x]+size[y];
        while kkk>=0 do
        begin
            if (zgdx-size[bz[cqy,kkk]]>zgdx div 2) and
               (sd[bz[cqy,kkk]]>=sd[op]) then cqy:=bz[cqy,kkk];
            dec(kkk);
        end;
        while zgdx-size[cqy]>zgdx div 2 do cqy:=fa[cqy];
        dis:=sd[cqy]-sd[op];
        ans:=kd[cqy]-(kd[op]-jd[op])-(n-size[op])*dis;
        ans:=ans+jd[x+y-op]+size[x+y-op]*(jgd+1+dis);
        123:
        writeln(ans);
    end;
end.
时间: 2024-09-27 07:13:00

JZOJ 4639 Angel Beats!【NOIP2016提高组A组7.16】的相关文章

【分享】Angel Beats! 遊戲合集(同人,試玩,ONS安卓體驗版,h同人 )

Angel Beats! 同人RPG游戏v1.0[無語音,帶BGM.角色扮演遊戲,漢化版] 預覽: 操作:[Z]菜單,確定,對話鍵,[X]返回,[Enter回車鍵]對話],[↑↓← →]行走鍵. 下載地址:http://www.400gb.com/file/81157758 AngelPlan!Ver.0试玩版[有語音和BGM,漢化版] 預覽: 下載地址:http://www.400gb.com/file/81157749 Angel Beats! TrackZERO 试玩版[無語音,有BGM,

判断用户的用户名和其基本组的组名是否一致

#!/bin/bash #传递一个用户名参数给脚本,判断此用户的用户名跟其基本组的组名是否一致,并将结果显示出来 #判断是否传递一个参数 if [ ! $# -eq 1 ]; then echo "Usage:./group.sh ARG" exit 1 fi #判断是否存在该用户 id $1 &> /dev/null if [ ! $? -eq 0 ]; then echo "$1 not exits." exit 1 fi #取给定用户所属基本组i

tableView组头 组尾滑动

今天布局tableview 要组头组尾滑动 从网上找的代码 很实用 留下来 每天进步一下 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.detailsTableView) { UITableView *tableview = (UITableView *)scrollView; CGFloat sectionHeaderHeight = 50; CGFloat sectionFoot

linux用户和组 只 组的管理

1. groupadd 新建组, 组名最长不能超过32个字节 groupadd -create a new group 语法: groupadd [option] 组名 -g, --gid GID        指定GID -r, --system          创建一个系统组 -o, --non-unique   此选项允许添加一个使用非唯一GID的组 [[email protected] ~]# groupadd admin [[email protected] ~]# groupadd

组播初涉,用户如何知道想加入哪一个组播组,如何知道要加入哪一个组播地址?

刚学到组播的时候,纠结了一个问题,用户如何知道想加入哪一个组播组,如何知道要加入哪一个组播地址? 现在想想真是,用四个字来形容就是我作为一个学计算机的“不够专业”. 这个问题其实很好回答,只是没想清楚而已,其实他等价于一个问题,我在看网页,发qq的时候,看B站的时候,我的手机是如何知道对方的IP地址从而获取数据的呢? 哈哈哈哈哈哈,那就是,码农们已经给你写好了,你用的软件其实到哪获取数据都已经是被写进去了,比如,腾讯的服务器是xxxx,你发qq的时候手机根据写好的程序 就自动与xxxx联系了.

独家讲解分析《组三组六 必中技巧》助你快速掌握

组三组六 必中技巧筘[5926656]胜率95%,已助上千人成功翻盘,他都是有问必答的. 号码直落定位组三,对应看百位号码,只要上下两期出现相同的号码,形成同位直落,俗称两期百位“对子码”结构,第三期的开jiang号码结构形态就可以重点考虑组三. 例如:50期jiang号为570,51期jiang号为543,百位号码5出现了直落,52期开出组三号码933.当然这种情形不是绝对的,有时会出现特殊形态的号码组合,如全大.全小.全质.全合等虽然出现了号码直落现象,但在之后出现的不是组三,而是全质数组合

JZOJ 4638 第三条跑道 【NOIP2016提高组A组7.16】

第三条跑道 该题目的名字是一首歌 题目大意 输入格式 输出格式 对于每个询问,单独一行输出答案. 样例输入 5 2 3 4 5 6 3 1 1 5 0 2 3 6 1 2 3 样例输出 32 48 数据范围 题解 我们先看一下φ的通式. 其中p1, p2--pn为x的所有质因数,x是不为0的整数. 再看一下数据范围,,这也就意味着ai在任何时刻都满足它的素因子是600以内的,而φ(ai)只跟它的素因子有关. 因为是区间查询/修改,当然是开线段树了. 600以内的素因子有109个,所以我们就种10

【NOIP2016提高组】愤怒的小鸟(状压宽搜)

题目描述 Kiana最近沉迷于一款神奇的游戏无法自拔. 简单来说,这款游戏是在一个平面上进行的. 有一架弹弓位于(0,0)处,每次Kiana可以用它向第一象限发射一只红色的小鸟,小鸟们的飞行轨迹均为形如的曲线,其中a,b是Kiana指定的参数,且必须满足a<0. 当小鸟落回地面(即x轴)时,它就会瞬间消失. 在游戏的某个关卡里,平面的第一象限中有n只绿色的小猪,其中第i只小猪所在的坐标为(xi,yi). 如果某只小鸟的飞行轨迹经过了(xi,yi),那么第i只小猪就会被消灭掉,同时小鸟将会沿着原先

noip2016——提高组——蚯蚓

大概这题难度提高+省选-. 我也做了半天. 这题如果用优先队列做的话会时间超限. 所以就要用另一种巧妙的做法. 见代码-- 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int read(){ 6 int t=1,num=0;char c=getchar(); 7 while(c>'9'||c<'0'){if(c=='-')t=-1