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)<<‘=‘<<(x)<<endl)
#define inf 0x3f3f3f3f
#define CLR(A,v)  memset(A,v,sizeof A)
/////////////////////////////////////
const int N=1e5+100;

int t[N<<5],b[N],lson[N<<5],rson[N<<5],T[N],pos,head[N],ncnt,n,ans[N],node[N],nn;
struct Edge{int to,nex;}edge[N<<1];
void add(int a,int b){edge[++pos]=(Edge){b,head[a]};head[a]=pos;}
void upnode(int x,int l,int r,int &pos)
{
    if(!pos)pos=++ncnt;
    if(l==r){t[pos]++;return ;}
    int m=(l+r)>>1;
    if(x<=m)upnode(x,l,m,lson[pos]);
    else upnode(x,m+1,r,rson[pos]);
    t[pos]=t[lson[pos]]+t[rson[pos]];
}
int qsum(int L,int R,int l,int r,int pos)
{
    if(L<=l&&r<=R)return t[pos];
    int m=(l+r)>>1;int ans=0;
    if(L<=m)ans+=qsum(L,R,l,m,lson[pos]);
    if(R>m)ans+=qsum(L,R,m+1,r,rson[pos]);
    return ans;
}
int Merge(int a,int b,int l,int r)
{
    if(!a)return b;
    if(!b)return a;
    if(l==r)
    {
        t[a]+=t[b];
        return a;
    }
    int m=(l+r)>>1;
    lson[a]=Merge(lson[a],lson[b],l,m);
    rson[a]=Merge(rson[a],rson[b],m+1,r);
    t[a]=t[lson[a]]+t[rson[a]];
    return a;
}

void dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nex)
    {
        int v=edge[i].to;
        dfs(v);
        T[x]=Merge(T[x],T[v],1,nn);
    }
    ans[x]=qsum(node[x],nn,1,nn,T[x]);
    upnode(node[x],1,nn,T[x]);
}
int main()
{
    scanf("%d",&n);
    rep(i,1,n)
    scanf("%d",&node[i]),b[i]=node[i],T[i]=i,ncnt++;
    sort(b+1,b+1+n);
    nn=unique(b+1,b+1+n)-b-1;
    rep(i,1,n)node[i]=lower_bound(b+1,b+1+nn,node[i])-b;

    rep(i,2,n)
    {
        int x;scanf("%d",&x);add(x,i);
    }
    dfs(1);

    rep(i,1,n)
    printf("%d\n",ans[i]);
    return 0;
}

其实也可以树状数组秒解

递归子树的时候先减去树状数组原有的信息即可

#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)<<‘=‘<<(x)<<endl)
#define inf 0x3f3f3f3f
#define CLR(A,v)  memset(A,v,sizeof A)
/////////////////////////////////////
const int N=1e5+100;

int t[N<<5],b[N],lson[N<<5],rson[N<<5],T[N],pos,head[N],ncnt,n,ans[N],node[N],nn;
struct Edge{int to,nex;}edge[N<<1];
void add(int a,int b){edge[++pos]=(Edge){b,head[a]};head[a]=pos;}
void upnode(int x,int l,int r,int &pos)
{
    if(!pos)pos=++ncnt;
    if(l==r){t[pos]++;return ;}
    int m=(l+r)>>1;
    if(x<=m)upnode(x,l,m,lson[pos]);
    else upnode(x,m+1,r,rson[pos]);
    t[pos]=t[lson[pos]]+t[rson[pos]];
}
int qsum(int L,int R,int l,int r,int pos)
{
    if(L<=l&&r<=R)return t[pos];
    int m=(l+r)>>1;int ans=0;
    if(L<=m)ans+=qsum(L,R,l,m,lson[pos]);
    if(R>m)ans+=qsum(L,R,m+1,r,rson[pos]);
    return ans;
}
int Merge(int a,int b,int l,int r)
{
    if(!a)return b;
    if(!b)return a;
    if(l==r)
    {
        t[a]+=t[b];
        return a;
    }
    int m=(l+r)>>1;
    lson[a]=Merge(lson[a],lson[b],l,m);
    rson[a]=Merge(rson[a],rson[b],m+1,r);
    t[a]=t[lson[a]]+t[rson[a]];
    return a;
}
void dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nex)
    {
        int v=edge[i].to;
        dfs(v);
        T[x]=Merge(T[x],T[v],1,nn);
    }
    ans[x]=qsum(node[x],nn,1,nn,T[x]);
    upnode(node[x],1,nn,T[x]);
}
int main()
{
    scanf("%d",&n);
    rep(i,1,n)
    scanf("%d",&node[i]),b[i]=node[i],T[i]=i,ncnt++;
    sort(b+1,b+1+n);
    nn=unique(b+1,b+1+n)-b-1;
    rep(i,1,n)node[i]=lower_bound(b+1,b+1+nn,node[i])-b;

    rep(i,2,n)
    {
        int x;scanf("%d",&x);add(x,i);
    }
    dfs(1);

    rep(i,1,n)
    printf("%d\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/bxd123/p/11411753.html

时间: 2024-10-24 03:50:10

P3605 [USACO17JAN]Promotion Counting晋升者计数 线段树合并 or 树状数组的相关文章

洛谷P3605 [USACO17JAN]Promotion Counting晋升者计数

洛谷P3605 [USACO17JAN]Promotion Counting晋升者计数 题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 $1 \cdots N(1 \leq N \leq 100, 000)$ 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点).除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 “双亲结点”).所有的第 $i$ 头牛都有一个不同的能力指数 $p(i)$,描述了她对其工作的擅长程度

[线段树合并] 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(

题解 P3605 【[USACO17JAN]Promotion Counting晋升者计数】

这道题开10倍左右一直MLE+RE,然后尝试着开了20倍就A了...窒息 对于这道题目,我们考虑使用线段树合并来做. 所谓线段树合并,就是把结构相同的线段树上的节点的信息合在一起,合并的方式比较类似左偏树什么的. 我们对于每个节点用权值线段树查询大于它的子节点数量,然后把当前节点并到它的父亲上面去. 对于此类型的题目我们通常使用动态开点的线段树(不然炸的没边). 时间复杂度应该是O(nlogn) AC代码如下: 455ms 32824kb 1 #include <bits/stdc++.h>

[USACO17JAN]Promotion Counting晋升者计数

题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 1 \cdots N(1 \leq N \leq 100, 000)1?N(1≤N≤100,000) 编号,把公司组织成一棵树,1 号奶牛作为总裁(这棵树的根节点).除了总裁以外的每头奶牛都有一个单独的上司(它在树上的 "双亲结点").所有的第 ii 头牛都有一个不同的能力指数 p(i)p(i),描述了她对其工作的擅长程度.如果奶牛 ii 是奶牛 jj 的祖先节点(例如

[USACO17JAN] Promotion Counting晋升者计数 (树状数组+dfs)

题目大意:给你一棵树,求以某节点为根的子树中,权值大于该节点权值的节点数 本题考查dfs的性质 离散+树状数组求逆序对 先离散 我们发现,求逆序对时,某节点的兄弟节点会干扰答案 所以,我们在递推时统计一次答案,递归时再统计一次答案,两者的差值就是最终结果 #include <bits/stdc++.h> #define dd double #define N 100100 using namespace std; int n,cnt,ma,lst; int a[N],head[N],s[N],

2016湖南省赛 I Tree Intersection(线段树合并,树链剖分)

2016湖南省赛 I Tree Intersection(线段树合并,树链剖分) 传送门:https://ac.nowcoder.com/acm/contest/1112/I 题意: 给你一个n个结点的树,树上每个节点有自己的颜色 问你删除第i条边后形成的两颗子树有多少个相同的颜色 题解: 树链剖分写法: 对于每一种颜色来说,如果这个颜色是在单独的一颗子树中,那么就不会对其他的边产生贡献,所以我们单独对每一种颜色对边的贡献讨论,如果这个颜色只有一个,那么就不会产生贡献,否则,他就可以在两个相同颜

[POI2011]ROT-Tree Rotations 线段树合并|主席树 / 逆序对

题目[POI2011]ROT-Tree Rotations [Description] 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有\(n\)个叶子节点,满足这些权值为\(1..n\)的一个排列).可以任意交换每个非叶子节点的左右孩子. 要求进行一系列交换,使得最终所有叶子节点的权值按照中序遍历写出来,逆序对个数最少. [Input Format] 第一行一个整数\(n\). 下面每行,一个数\(x\). 如果\(x =0\),表示这个节点非叶子节点,递归地向下读

USACO17JAN Promotion Counting

题目传送门 简化题意:一颗带有点权.以\(1\)为根的树,对于每个节点\(x\),求出\(x\)的子树中有多少个点满足该点的点权大于\(x\)的点权 先将点权离散化 对这棵树进行DFS,在DFS到\(x\)时,加入该点点权,然后在DFS它的子树前记录一下当前有多少节点大于\(x\),记为\(last\).在回溯到该节点时再次查询当前有多少节点大于\(x\),减去\(last\)即为答案 #include <iostream> #include <cstdio> #include &

线段树合并浅谈

对于某些对子树的统计问题,我们固然可以用DSU on Tree来解决,但是一旦带上修改,甚至是加上历史化版本的查询,我们就不得不求助于其他的算法,本篇将对线段树合并进行讲解 线段树合并一般用于对子树的统计,一般的套路就是对树的每一个节点都开上一颗动态开点线段树,然后统计子树信息时,合并所有儿子信息,统计答案,然后继续向上走; 例题也很多,比如[USACO17JAN]Promotion Counting晋升者计数,[NOIP2016 DAY1]天天爱跑步等等都可以这样乱搞,实现起来也非常好理解,这