NOIP训练营内部训练题-三部曲(附解析代码)

网友投稿 2019-08-14 11:52

三部曲(内部试题)

建议同学们先做题再看答案      

【问题描述】 

因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为0。当城市i被加派了k名士兵时。城市i的所有子城市需要被加派k+1名士兵。这些子城市的所有子城市需要被加派k+2名士兵。以此类推。当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市i为根的子树中的所有城市共被加派了多少士兵。 你现在是国王的军事大臣,你能回答出国王的每个询问么? 

【输入格式】 

第一行,包含两个整数N,P代表城市数量以及国王的命令的数量。 第二行N-1个整数,表示2-N号每个节点的父亲节点。 接下来的P行,每行代表国王的一个命令,命令分两种: A X K 在城市X加入K个士兵 Q X 询问以城市X为根的子树中所有士兵数量的和。 

【输出格式】 

对于每个Q,输出答案。 

【样例输入】

https://cdn.china-scratch.com/timg/190816/1152141L7-0.jpg

 【样例输出】

https://cdn.china-scratch.com/timg/190816/1152141c4-1.jpg

【数据规模与约定】 

对于50%的数据,1≤N≤1000 1≤P≤300。 对于100%的数据,1≤N≤50000 1≤P≤100000 1≤X≤N 0≤K≤1000。

题解出自清北NOIP训练营学员,详情见:https://i-jvxie.com/archives/67.htm

l题目还算是好理解的,然而看看数据范围,朴素的 dfs 只能拿前 50 分
所以我们要相处一点高效的方法,比如 log 级别的时间复杂度,那么很容易就能联想到线段树
看样子是很难做线段树,因为这是个树结构的图而非线性结构
但是!!有种神奇的方式可以将树结构转化为线性结构————dfs 序

题解:dfs 预处理 + 线段树

dfs 一趟可以处理出每个点的 dfs 序,也可以知道这个点下还有多少个节点
那么我们就可以得出从每个点开始的区间为哪一个,再借助线段树就可以轻松做到区间求和 / 修改等操作了
但是还有个问题————节点往下要每次加上 1,该如何解决呢?
其实我们可以在处理 dfs 序时顺便处理出若根节点(1)加 0 时,每个节点会加多少,这里设为 h
然后用线段树求出每个区间 h 的和,以便之后的另一棵线段树使用
然后每次在 A 操作的时候,将要加的 k 值减去这个节点的 h,在区间修改的时候加上区间的 h 和
那么就相当于我们做完了节点加数的递增操作

时间复杂度

大概的时间复杂度可以这么计算:
dfs 预处理 O(n)+ 线段树建树 O(n)+ 线段树操作总数 (plogn)=O(2n+plogn)
还是远远小于 1s 的~
至此,我们也就得出了一个正解,下面放上完整代码

C++

1

#include

#include

#include

#include

#define N 100010

#define ll long long

#define fmid (l+r)>>1 

#define lson l,mid,p<<1

#define rson mid+1,r,(p<<1)+1 

struct edge{

    int to,go;

}road[N];

//tree为要求的和,tree_h为树层和 

struct Tree{

    int l,r,eps;

    ll tot,h;

}tree[N<<2],tree_h[N<<2];

int len,dfs_order[N],dfs_son[N],dfs_dev[N],head[N],ch[N];

bool vis_dfs[N];

//边表处理 

void add(int a,int b){

    ++len;

    road[len].to=b;

    road[len].go=head[a];

    head[a]=len;

    return ;

}

//dfs预处理 

void dfs(int a,int dev){

    int i=head[a],b,ret=0;

    ++len;

    dfs_order[a]=len;

    vis_dfs[a]=1;

    dfs_dev[len]=dev;

    while(i!=-1){

        b=road[i].to;

        dfs(b,dev+1);

        ret+=dfs_son[b]+1;

        i=road[i].go;

    }

    dfs_son[a]=ret;

    return ;

}

//建树 

void build(int l,int r,int p){

    if(l==r){

        tree_h[p].tot=dfs_dev[l];

        tree_h[p].l=tree_h[p].r=l;

        tree[p].l=tree[p].r=r;

        return ;

    }

    int mid=(l+r)>>1;

    build(lson);build(rson);

    tree_h[p].l=tree[p].l=l;

    tree_h[p].r=tree[p].r=r;

    tree_h[p].tot=tree_h[p<<1].tot+tree_h[(p<<1)+1].tot;

    return ;

}

//区间修改 

void push_down(int p){

    if(tree[p].eps){

        tree[p<<1].h+=tree[p].h,tree[p<<1].eps+=tree[p].eps;

        tree[(p<<1)+1].h+=tree[p].h,tree[(p<<1)+1].eps+=tree[p].eps;

        int mid=(tree[p].l+tree[p].r)>>1;

        tree[p<<1].tot+=tree[p].h*(mid-tree[p].l+1)+tree[p].eps*tree_h[p<<1].tot;

        tree[(p<<1)+1].tot+=tree[p].h*(tree[p].r-mid)+tree[p].eps*tree_h[(p<<1)+1].tot;

        tree[p].eps=0,tree[p].h=0;

    }

    return ;

}

void update(int kl,int kr,int p,int k){

    int l=tree[p].l,r=tree[p].r;

    if(l>=kl && kr>=r){

        tree[p].h+=k;

        tree[p].eps++;

        tree[p].tot+=tree_h[p].tot+(r-l+1)*k;

        return ;

    }

    push_down(p);

    int mid=fmid;

    if(kl<=mid) update(kl,kr,p<<1,k);

    if(kr>mid) update(kl,kr,(p<<1)+1,k);

    tree[p].tot=tree[p<<1].tot+tree[(p<<1)+1].tot;

    return ;

}

//区间查询 

ll query(int kl,int kr,int p){

    int l=tree[p].l,r=tree[p].r;

    if(l>=kl && kr>=r) return tree[p].tot;

    push_down(p);

    int mid=fmid;

    if(kl<=mid){

        if(mid<<1)+query(kl,kr,(p<<1)+1);<="" p="">

        else return query(kl,kr,p<<1);

    }

    else return query(kl,kr,(p<<1)+1);

}

int main(){

//  freopen("truetears.in","r",stdin);

//  freopen("truetears.out","w",stdout);

    memset(head,-1,sizeof(head));

    int n,p,i,a,b;

    char s[5];

    scanf("%d%d",&n,&p);

    for(i=2;i<=n;i++){

        scanf("%d",&a);

        add(a,i);

    }

    len=0;

    for(i=1;i<=n;i++)

        if(!vis_dfs[i])

            dfs(i,0);

    build(1,n,1);

    while(p--){

        scanf("%s",s);

        switch(s[0]){

            case 'A':{

                scanf("%d%d",&a,&b);

                b-=dfs_dev[dfs_order[a]]; 

                printf("b = %dn",b);

                update(dfs_order[a],dfs_order[a]+dfs_son[a],1,b);

                break ;

            }

            case 'Q':{

                scanf("%d",&a);

                printf("%lldn",query(dfs_order[a],dfs_order[a]+dfs_son[a],1));

                break ;

            }

        }

    }

    return 0;

}

END

--end--

声明:本文章由网友投稿作为教育分享用途,如有侵权原作者可通过邮件及时和我们联系删除:freemanzk@qq.com