【usaco2017Jan】Promotion Counting的解析——线段树合并入门

这道题貌似是道水题,看见那么多人写树状数组,正好我最近又要学习线段树合并,于是发了这篇线段树合并入门blog.

一:线段树合并的概念.

首先我们先介绍完线段树合并再来分析题目.

线段树合并是基于动态开点线段树的,一开始创建n棵线段树,每棵线段树只有一条链是记录着的.

现在我们不断的两两合并这些线段树,当这两棵线段树有共同节点时,合并信息,其它的直接合并就可以了.

就像这两棵线段树就可以这么合并(节点上的数值表示权值,假设我们现在维护的是区间和,求没有的点都是0):

那么这样子做看上去很暴力,其实时间复杂度和空间复杂度都是比较优秀的,均为O(nlog(n)).

二:复杂度证明.

首先空间复杂度由于合并的时候空间只会减少,而初始每棵树都是O(log(n))的空间,所以空间是O(nlog(n)).

而时间复杂度最初建树是O(nlog(n))的,但是只有而每次不需要进行信息叠加的节点是不需要耗费复杂度的,进行叠加的节点最多是最初的O(nlog(n))个节点减掉最后的O(n)的节点,算起来也就是O(nlog(n))个节点,而每次叠加也是O(1)的,所以时间复杂度为O(nlog(n)),总的时间复杂度也就是O(nlog(n)).

三:实现线段树合并.

线段树合并写过主席树的话应该就很简单了,我们先建立n棵线段树,每棵线段树都是一条链.

然后每次合并的时候,直接同事遍历两棵线段树,当节点还是重合的时候,我们合并区间信息,不重合的时候,我们直接把第二棵线段树上的信息链接到第一棵上.

这样还是比较简单的,具体代码看题目分析.

四:解决题目.

题目:luogu3605.

题目大意:现在有一棵树,每个节点i有一个点权v[i],现在我们要求求出每一个节点i为根的子树中有多少的节点j满足v[j]>v[i].

那么这道题其实还是很模板的.

我们先考虑每个节点都建立一棵动态开点权值线段树,开始只有一条链,由于数值范围很大,我们需要先离散化.

现在我们深度优先遍历这棵树,每一次遍历完一个节点的一个儿子的时候,把这个儿子的线段树并到当前节点的线段树中.当遍历完所有儿子之后,我们直接存储在这可线段树中比当前节点权值大的权值数量就可以了.

代码实现如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
#define rep(i,j,k) for (int i=j;i<=k;i++)
typedef long long LL;
const int N=100000;
const int INF=(1<<30)-1+(1<<30);
struct tree{
  int ls,rs,l,r,sum;
}tr[N*50];
struct side{
  int y,next;
}e[N*2+9];
int n,p[N+9],lin[N+9],ts,top,root[N+9],order[N+9],cnt[N+9],ans[N+9];
void ins(int X,int Y){
  e[++ts].y=Y;
  e[ts].next=lin[X];
  lin[X]=ts;
}
int find(int k){
  int l,r,mid;
  for (l=1,r=n,mid=l+r>>1;l+1<r;mid=l+r>>1)
    k>order[mid]?l=mid:r=mid;
  return order[l]==k?cnt[l]:cnt[r];
}
void build(int L,int R,int x){
  ++top;
  tr[top].l=L;tr[top].r=R;tr[top].sum=1;
  if (L==R) return;
  int mid=L+R>>1;
  if (x<=mid) tr[top].ls=top+1,build(L,mid,x);
  else tr[top].rs=top+1,build(mid+1,R,x);
}
void merge(int u,int v){
  tr[u].sum+=tr[v].sum;
  if (tr[u].ls&&tr[v].ls) merge(tr[u].ls,tr[v].ls);
  else if (tr[v].ls) tr[u].ls=tr[v].ls;
  if (tr[u].rs&&tr[v].rs) merge(tr[u].rs,tr[v].rs);
  else if (tr[v].rs) tr[u].rs=tr[v].rs;
}
int query(int L,int R,int k){
  if (R<L||!k) return 0;
  if (L==tr[k].l&&R==tr[k].r) return tr[k].sum;
  int mid=tr[k].l+tr[k].r>>1;
  if (R<=mid) return query(L,R,tr[k].ls);
  else if (L>mid) return query(L,R,tr[k].rs);
    else return query(L,mid,tr[k].ls)+query(mid+1,R,tr[k].rs);
}
void dfs(int k,int fa){
  for (int i=lin[k];i;i=e[i].next)
    if (fa^e[i].y) { 
      dfs(e[i].y,k);
      merge(root[k],root[e[i].y]);
    }
  ans[k]=query(p[k]+1,n,root[k]);
}
Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;i++)
    scanf("%d",&p[i]),order[i]=p[i];
  int x;
  for (int i=2;i<=n;i++){
    scanf("%d",&x);
    ins(i,x);ins(x,i);
  }
}
Abigail work(){
  sort(order+1,order+1+n);
  for (int i=1;i<=n;i++)
    cnt[i]=order[i]==order[i-1]?cnt[i-1]:cnt[i-1]+1;
  for (int i=1;i<=n;i++)
    p[i]=find(p[i]);
  for (int i=1;i<=n;i++){
    root[i]=top+1;
    build(1,n,p[i]);
  }
  dfs(1,0);
}
Abigail outo(){
  for (int i=1;i<=n;i++)
    printf("%d\n",ans[i]);
}
int main(){
  into();
  work();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/81148398