[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;
}

猜你喜欢

转载自blog.csdn.net/guapisolo/article/details/81104258