程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 主席樹初探 & bzoj 3295: [Cqoi2011] 動態逆序對 題解

主席樹初探 & bzoj 3295: [Cqoi2011] 動態逆序對 題解

編輯:C++入門知識

【原題】

3295: [Cqoi2011]動態逆序對

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 778 Solved: 263
[Submit][Status]

Description

對於序列A,它的逆序對數定義為滿足i<j,且Ai>Aj的數對(i,j)的個數。給1到n的一個排列,按照某種順序依次刪除m個元素,你的任務是在每次刪除一個元素之前統計整個序列的逆序對數。

Input

輸入第一行包含兩個整數nm,即初始元素的個數和刪除的元素個數。以下n行每行包含一個1到n之間的正整數,即初始排列。以下m行每行一個正整數,依次為每次刪除的元素。

Output

輸出包含m行,依次為刪除每個元素之前,逆序對的個數。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

樣例解釋
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

N<=100000 M<=50000


【題外話】一直想A了這道題。真累啊!開始的想法是——倒著做,一個一個添加,套一個樹狀數組,然後每次用平衡樹去尋找。顯然,現在我只會SPLAY,而且代碼長,效率渣。今天和SYF大神一起看hzwer大神的博客,加之哲大爺的一語道破,總算會了樹狀數組套主席樹的方法。

【主要思路】首先用樹狀數組/歸並排序/歸並樹(時代的眼淚)求出總的序列中的逆序對個數。每次刪掉的時候,用樹狀數組維護位置(1--x-1或x+1--n),並且用權值線段樹維護權值信息。

【詳細算法】

①在之前的計算總的逆序對個數的時候,我們可以順帶的算出front[i]和back[i],表示第i個點之前有front[i]個數比他大,之後有back[i]個數比他小。

②起初剛開始做的時候,主席樹內部是空的。因此我們要轉化——我們本來計算的是刪掉某個數後,將會減少所剩的數列中的多少的逆序對(即對所剩的序列產生多少的貢獻);現在我們改成:刪掉某個數後,將會對主席樹中已經插入的一些節點產生多少的貢獻。這樣算出來後,用front或back減一下即可。

舉個例子。比如有數列5,4,3,2,1,已經刪掉了4和1,ans值是3。現在我們要刪掉3了。在主席樹中,已添加的節點是5和2。通過計算,5,3,2會在3的前面產生一個逆序對,在3的後面產生一個逆序對。而front[3]=2,back[3]=2,那麼我們可以知道,ans需要減去front[3]-1與back[3]-1,即最後ans只剩下了1。

③後來套的樹狀數組具體是怎麼實現的呢?比如我們要計算或更新l~r范圍內主席樹中的某個或某些權值的個數。我們可以轉化為計算或更新1--l-1與1--r的前綴,再相減即可。而前綴是可以用樹狀數組解決的。

【代碼】

#include
#include
#define N 100005
#define L(x) (x&-x)
using namespace std;
typedef long long LL;LL ans;
struct arr{int l,r;LL sum;}a[6000000];
int front[N],back[N],c[N],root[N],pos[N],data[N],A[31],B[31];
int n,node,del,x,m,i;
inline int ask(int x){int s=0;for (;x;x-=L(x)) s+=c[x];return s;}
inline void up(int x){for (;x<=n;x+=L(x)) c[x]++;}
inline LL ask_more(int l,int r,int num)
{
  if (l>r) return 0;l--;A[0]=B[0]=0;
  for (int i=l;i;i-=L(i)) A[++A[0]]=root[i];
  for (int i=r;i;i-=L(i)) B[++B[0]]=root[i];
  l=1;r=n;LL sum=0;
  while (l!=r)
  {
    int mid=(l+r)>>1;
    if (num<=mid) 
    {
      for (int i=1;i<=B[0];i++) sum+=a[a[B[i]].r].sum;
      for (int i=1;i<=A[0];i++) sum-=a[a[A[i]].r].sum;
      for (int i=1;i<=B[0];i++) B[i]=a[B[i]].l;
      for (int i=1;i<=A[0];i++) A[i]=a[A[i]].l;
      r=mid;
    }
    else
    {
      for (int i=1;i<=B[0];i++) B[i]=a[B[i]].r;
      for (int i=1;i<=A[0];i++) A[i]=a[A[i]].r;
      l=mid+1;
    }
  }
  return sum;
}
inline LL ask_less(int l,int r,int num)
{
  if (l>r) return 0;l--;A[0]=B[0]=0;
  for (int i=l;i;i-=L(i)) A[++A[0]]=root[i];
  for (int i=r;i;i-=L(i)) B[++B[0]]=root[i];
  l=1;r=n;LL sum=0;
  while (l!=r)
  {
    int mid=(l+r)>>1;
    if (num>mid)
    {
      for (int i=1;i<=B[0];i++) sum+=a[a[B[i]].l].sum;
      for (int i=1;i<=A[0];i++) sum-=a[a[A[i]].l].sum;
      for (int i=1;i<=B[0];i++) B[i]=a[B[i]].r;
      for (int i=1;i<=A[0];i++) A[i]=a[A[i]].r;
      l=mid+1;
    }
    else
    {
      for (int i=1;i<=B[0];i++) B[i]=a[B[i]].l;
      for (int i=1;i<=A[0];i++) A[i]=a[A[i]].l;
      r=mid;
    }
  }
  return sum;
}
inline void update(int &k,int l,int r)
{
  if (!k) k=++node;a[k].sum++;
  if (l==r) return;
  int mid=(l+r)>>1;
  if (del<=mid) update(a[k].l,l,mid);
  else update(a[k].r,mid+1,r);
}
inline int Read()
{
  char ch=getchar();for (;ch<'0'||ch>'9';ch=getchar());
  int x=0;for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
  return x;
}
int main()
{
  n=Read();m=Read();
  for (i=1;i<=n;i++)
  {
    data[i]=Read();pos[data[i]]=i;
    ans+=(front[i]=i-1-ask(data[i]));up(data[i]);
  }
  memset(c,0,sizeof(c));
  for (i=n;i;i--)
    back[i]=ask(data[i]-1),up(data[i]);
  for (i=1;i

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved