1. 程式人生 > >Luogu P3605 [USACO17JAN]Promotion Counting 線段樹合併

Luogu P3605 [USACO17JAN]Promotion Counting 線段樹合併

題目描述

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…N (1≤N≤100,000), organize the company as a tree, with cow 1 as the president (the root of the tree). Each cow except the president has a single manager (its “parent” in the tree). Each cow i has a distinct proficiency rating, p(i), which describes how good she is at her job. If cow i is an ancestor (e.g. a manager of a manager of a manager) of cow j, then we say j is a subordinate of i.

Unfortunately, the cows find that it is often the case that a manager has less proficiency than several of her subordinates, in which case the manager should consider promoting some of her subordinates. Your task is to help the cows figure out when this is happening. For each cow i in the company, please count the number of subordinates j where p(j) > p(i).

輸入格式: The first line of input contains N.

The next N lines of input contain the proficiency ratings p(1) … p(N) for the cows. Each is a distinct integer in the range 1…1,000,000,000.

The next N-1 lines describe the manager (parent) for cows 2…N. Recall that cow 1 has no manager, being the president.

輸出格式: Please print N lines of output. The ith line of output should tell the number of subordinates of cow ii with higher proficiency than cow ii.

題意:給定一棵樹,求每個點子節點權值大於本身權值的數量。

思路

離散化+動態開點+權值線段樹+線段樹合併 首先將所有節點p的值離散化,建一棵權值線段樹儲存p的值(權值線段樹中存在區間內值的數量)。 dfs搜到葉子節點後回溯,每次把孩子節點和父親節點線上段樹中合併(合併過程見下文)。 最後進行區間查詢大於節點權值的數量。 此處線段樹需用動態開點,並注意線段樹大小要開到30倍左右。

線段樹合併原理

線段樹合併:把兩個線段樹節點合併為一個,儲存的資訊整合在一起。 此題中線段樹中儲存的資訊是某區間內值的數量,所以整合時把兩權值相加即可。 假設要將u,v合併, 若u或v為空,則返回另一節點。 否則建一新節點t,整合u,v資訊(此題中為兩權值相加),再遞迴合併u,v的子樹。 合併時需注意維護權值資訊。

程式碼

#include<stdio.h>
#include<cstring>
#include<algorithm>
#define re register int
#define fast static int
using namespace std;
typedef long long ll;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0' || ch>'9')
    {
        if(ch=='-')	f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
    {
        x=10*x+ch-'0';
        ch=getchar();
    }
    return x*f;
}
const int Size=100005;
int n,p[Size],np[Size];
/*p:p的值,經離散化後會改變		np:離散化中排序用的陣列*/
struct Edge
{
    int v,next;
} w[Size<<1];			//存邊 
int cnt,head[Size];		//鏈式前向星 
inline void AddEdge(int u,int v)
{
    w[++cnt].v=v;
    w[cnt].next=head[u];
    head[u]=cnt;
}
inline int search(int x)		//二分查詢np中x的位置(離散化用) 
{
    re l=1,r=n,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(np[mid]==x)
        {
            return mid;
        } else if(np[mid]<x) {
            l=mid+1;
        } else {
            r=mid-1;
        }
    }
    return l-1;
}
int root[Size],tot,ans[Size];	//root[x]表示x線上段樹中位置 
								//tot表示當前線段樹中節點總個數 
								//ans記錄答案 
struct node {
    int l,r,v;		//當前區間和權值 
    int lc,rc;		//左孩子和右孩子 
} tree[Size*30];
inline void Pushup(int rt)
{
    tree[rt].v=tree[tree[rt].lc].v+tree[tree[rt].rc].v;
}
void Insert(int l,int r,int x,int &rt)		//線上段樹中插入節點x 
{
    rt=++tot;
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r)
    {
        tree[rt].v=1;
        return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)	Insert(l,mid,x,tree[rt].lc);
    if(x>mid)	Insert(mid+1,r,x,tree[rt].rc);
    Pushup(rt);
}
int Query(int l,int r,int rt)			//查詢權值在區間[l,r]內的p的個數 
{
//	printf("%d %d %d\n",l,r,rt);
    if(!rt)	return 0;
    if(l<=tree[rt].l && tree[rt].r<=r)
    {
        return tree[rt].v;
    }
    int ans=0,mid=(tree[rt].l+tree[rt].r)>>1;
    if(l<=mid)	ans+=Query(l,r,tree[rt].lc); 
    if(r>mid)	ans+=Query(l,r,tree[rt].rc);
    return ans;
}
int merge(int x,int y)			//合併x,y 
{
    if(x==0 || y==0)	return x|y;
    if(tree[x].l==tree[x].r)
    {
        tree[x].v+=tree[y].v;
        return x;
    }
    tree[x].lc=merge(tree[x].lc,tree[y].lc);
    tree[x].rc=merge(tree[x].rc,tree[y].rc);
    Pushup(x);
    return x;
}
void dfs(int x)					//dfs 
{
    for(int i=head[x]; i; i=w[i].next)
    {
    	dfs(w[i].v);
    	root[x]=merge(root[x],root[w[i].v]);
    }
    ans[x]=Query(p[x]+1,n+1,root[x]);
}
void init()
{
    n=read();
    for(re i=1; i<=n; i++)
    {
        p[i]=read();
    }
    for(re i=2; i<=n; i++)
    {
    	int x=read();
        AddEdge(x,i);
    }
    /*離散化*/
    for(re i=1; i<=n; i++)
    {
        np[i]=p[i];
    }
    n=unique(np+1,np+1+n)-(np+1);
    sort(np+1,np+1+n);
    for(re i=1; i<=n; i++)
    {
        p[i]=search(p[i]);
        Insert(1,n+1,p[i],root[i]);
    }
}
int main()
{
    init();
    dfs(1);
    for(re i=1; i<=n; i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}