[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],ans[N];
struct EDGE{
    int to,nxt;
}edge[N*2];
struct node{
    int og,mx,id;
}d[N];
int cmp1(node a,node b) {return a.og<b.og;}
int cmp2(node a,node b) {return a.id<b.id;}
void update(int x,int p)
{
    for(int i=x;i<=ma;i+=(i&(-i)))
    {
        s[i] += p;
    }
}
int query(int x)
{
    int ans=0;
    for(int i=x;i>0;i-=(i&(-i)))
    {
        ans += s[i];
    }
    return ans;
}
void edge_add(int u,int v)
{
    cnt++;
    edge[cnt].to = v;
    edge[cnt].nxt= head[u];
    head[u] = cnt;
}
void discrete()
{
    sort(d+1,d+n+1,cmp1);
    for(int i=1;i<=n;i++)
    {
        if(d[i].og==d[i-1].og)
        {
            d[i].mx=d[i-1].mx;
        }else{
            d[i].mx=++ma;
        }
    }
    sort(d+1,d+n+1,cmp2);
    for(int i=1;i<=n;i++) a[i] = d[i].mx;
}
void dfs(int x,int fa)
{
    for(int j=head[x];j!=-1;j=edge[j].nxt)
    {
        int v=edge[j].to;
        if(v==fa) continue;
        int s1=query(ma)-query(a[x]);
        dfs(v,x);
        int s2=query(ma)-query(a[x]);
        ans[x] += s2-s1;
    }
    update(a[x],1);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {scanf("%d",&d[i].og);d[i].id=i;}
    memset(head,-1,sizeof(head));
    int x;
    for(int i=2;i<=n;i++)
    {
        scanf("%d",&x);
        edge_add(i,x);
        edge_add(x,i);
    }
    discrete();
    dfs(1,-1);
    for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/guapisolo/p/9678835.html

时间: 2024-08-30 07:27:51

[USACO17JAN] Promotion Counting晋升者计数 (树状数组+dfs)的相关文章

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

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

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)<<'='

[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 的祖先节点(例如

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

【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序

[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 我们把纸上打印出来的字符串从1开始顺序编号,一直到n.打字机有一个非

4.4 省选模拟赛 拉格朗日计数 树状数组+容斥.

像这种计数 问题什么的 是最讨厌的了... 考虑是环往环后面再续一段 暴力枚举前两个数字 树状数组统计第三个数的个数 n^2log. 考虑只枚举第个数 发现由于边界问题什么的很难处理. 再将枚举直接放到环上 发现边界问题没有了 不过存在 枚举第二个数之后 有 123 231 312 这三种形式. 第一种形式很好统计 预处理一下左边有多少个数字比自己小即可. 考虑第二种和第三种形式 很难在枚举2的时候统计出来这两种形式 考虑容斥 231=XX1-321. 312=3XX-321. 发现XX1和3X

HDU 5862 Counting Intersections (离散化+扫描线+树状数组)

题意:给你若干个平行于坐标轴的,长度大于0的线段,且任意两个线段没有公共点,不会重合覆盖.问有多少个交点. 析:题意很明确,可是并不好做,可以先把平行与x轴和y轴的分开,然后把平行y轴的按y坐标从小到大进行排序,然后我们可以枚举每一个平行x轴的线段, 我们可以把平行于x轴的线段当做扫描线,只不过有了一个范围,每次要高效的求出相交的线段数目,可以用一个优先队列来维护平行y轴的线段的上坐标, 如果在该平行于x轴的范围就给相应的横坐标加1,这样就很容易想到是用树状数组来维护,然后每次求出最左边和最右边

BZOJ2434 NOI2011 阿狸的打字机 AC自动机+树状数组+DFS序

题意:给定三个操作:1.在当前字符串的末尾添加一个字符c  2.在当前字符串的末尾删除一个字符  3.记录当前字符串并对其标号.再给出N组询问,每组询问需回答第x个字符串在第y个字符串中出现的次数 题解: 首先按照如下规则建Trie,设当前节点为t,第i个字符串的结尾在Trie中的位置为mark[i]: 1.插入操作:看t是否有c这个儿子,有则t=t->child[c],否则t->child[c]=NewNode,t=t->child[c] 2.删除操作:t=t->father 3