BZOJ 4756 [Usaco2017 Jan]Promotion Counting(线段树合并)

【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=4756

【题目大意】

  给出一棵树,对于每个节点,求其子树中比父节点大的点个数

【题解】

  我们考虑每个权值建立一棵线段树,边dfs边将子节点合并为一颗线段树,
  那么只要查询当前点的树上后缀和即可。

【代码】

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N=100010,M=N*20;
int n,a[N],ans[N],root[N],disc[N];
vector<int> v[N];
namespace Segment_Tree{
    int tot;
    struct node{int l,r,a,b,sum;}T[M];
    void up(int x){T[x].sum=T[T[x].l].sum+T[T[x].r].sum;}
    int build(int l,int r,int p){
        int x=++tot;
        T[x].a=l; T[x].b=r; T[x].sum=0;
        if(l==r){T[x].sum=1;return x;}
        int mid=(l+r)>>1;
        if(p<=mid){T[x].l=build(l,mid,p);}
        else{T[x].r=build(mid+1,r,p);}
        return up(x),x;
    }
    int ask(int x,int l,int r){
        if(!x)return 0;
        if(l<=T[x].a&&T[x].b<=r)return T[x].sum;
        int mid=(T[x].a+T[x].b)>>1,res=0;
        if(l<=mid)res+=ask(T[x].l,l,r);
        if(r>mid)res+=ask(T[x].r,l,r);
        return res;
    }
    int merge(int x,int y){
        if(!x||!y)return x^y;
        T[x].l=merge(T[x].l,T[y].l);
        T[x].r=merge(T[x].r,T[y].r);
        return up(x),x;
    }
    void dfs(int x,int fx){
        int res=0;
        for(int i=0;i<v[x].size();i++){
            int y=v[x][i];
            if(y==fx)continue;
            dfs(y,x);
            res+=ask(root[y],a[x]+1,n);
            root[x]=merge(root[x],root[y]);
        }ans[x]=res;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),disc[i]=a[i];
    sort(disc+1,disc+n+1);
    int m=unique(disc+1,disc+n+1)-disc-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(disc+1,disc+m+1,a[i])-disc;
    for(int i=2;i<=n;i++){
        int x; scanf("%d",&x);
        v[x].push_back(i); v[i].push_back(x);
    }
    for(int i=1;i<=n;i++)root[i]=Segment_Tree::build(1,n,a[i]);
    Segment_Tree::dfs(1,1);
    for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
    return 0;
}
时间: 2024-11-01 13:36:40

BZOJ 4756 [Usaco2017 Jan]Promotion Counting(线段树合并)的相关文章

【bzoj4756】[Usaco2017 Jan]Promotion Counting 离散化+树状数组

原文地址:http://www.cnblogs.com/GXZlegend/p/6832263.html 题目描述 The cows have once again tried to form a startup company, failing to remember from past experience that cows make terrible managers!The cows, conveniently numbered 1-N1-N (1≤N≤100,000), organi

[BZOJ4756][Usaco2017 Jan]Promotion Counting 树状数组

4756: [Usaco2017 Jan]Promotion Counting Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 305  Solved: 217[Submit][Status][Discuss] Description The cows have once again tried to form a startup company, failing to remember from past experience t hat cow

[BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】

题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换左右子树无关,是否交换左右子树取决于交换后 “跨越 x 左子树与右子树的逆序对” 是否会减小. 因此我们要求出两种情况下的逆序对数,使用线段树合并,对每个节点建一棵线段树,然后合并的同时就求出两种情况下的逆序对. 代码 #include <iostream> #include <cstdli

bzoj 2212 : [Poi2011]Tree Rotations (线段树合并)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2212 思路:用线段树合并求出交换左右儿子之前之后逆序对的数量,如果数量变小则交换. 实现代码: #include<bits/stdc++.h> using namespace std; #define ll long long const int M = 4e5+10; int n,cnt,idx; ll ans,cnt1,cnt2; int v[M],l[M],r[M],root[

[BZOJ 3888] [Usaco2015 Jan] Stampede 【线段树】

题目链接:BZOJ - 3888 题目分析 首先,计算出每个线段在 x 坐标 0 处出现的时间开始点和结束点,就转成了时间轴上的线段. 然后就是看每条线段是否被 y 比它小的线段完全覆盖了.注意求出的时间点要离散化,然后应该使用时间轴上的区间来表示,两线段端点重合并不是有共同部分. 将所有线段按照 y 从小到大排序之后,使用线段树判断它覆盖的区间是否已经都被前面的线段覆盖了. 然后将它所覆盖的区间覆盖. 就这样的一道题我WA了7次,还有救吗.. 代码 #include <iostream> #

BZOJ 2212 [Poi2011]Tree Rotations(线段树合并)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2212 [题目大意] 给出一棵二叉树,每个叶节点上有一个权值,现在可以任意交换左右儿子, 使得逆序对最少,求最少的逆序对数量 [题解] 我们发现对于每个非叶节点来说,其贡献值为左右两个儿子的权值树上, 每个节点想反位置的数量和乘积,比如左儿子的权值树左节点和右儿子权值树的右节点相乘, 那么我们对于每个节点建立一颗权值线段树,仅保留非0链, 递归合并这些权值线段树,同时每次将相反位置数量

BZOJ 4756 线段树合并(线段树)

思路: 1.最裸的线段树合并 2. 我们可以观察到子树求一个东西 那我们直接DFS序好了 入队的时候统计一下有多少比他大的 出的时候统计一下 减一下 搞定~ 线段树合并代码: //By SiriusRen #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=100050; int n,col[N],cpy[N],tree[N*100],lso

P3605 [USACO17JAN]Promotion Counting晋升者计数 线段树合并 or 树状数组

题意:每个点有一个权值    求每个节点的子树中比其权值大的节点数 线段树合并模板题 #include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);--i) #define ll long long #define see(x) (cerr<<(#x)<<'='

[线段树合并] Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

给一棵 N 个点的树,每个点有一个权值,求每个点的子树中有多少个点的权值比它大. 考虑线段树合并,将权值离散化,每个点开一棵权值线段树. 求答案时直接在权值线段树上查询,线段树合并时类似于可并堆. 要注意的是线段树要动态开点,合并时别忘了 up. 内存什么的最好算一下,数组别开小了. 1 #include<bits/stdc++.h> 2 #define rep(i,a,b) for(register int i=a;i<=b;++i) 3 #define rpd(i,a,b) for(