pku 3378(树状数组实现)

原创 2007年09月13日 22:06:00

pku 3378树状数组解法:

看到题目,我们首先应该想到的是求:在一个数组里前n-1个数有多少个比第n个数小。
对于这个问题,我先将这个数组离散化---就是得到第n个数是第几大(或小)的数,然后建立树状
数组,s[i]为第i大的数前面的所有的数的个数(包括第i大的数),这样说或许不好理解,但是,
我解释一下以下的语句就简单多了:(前提是你已经对树状数组有所了解)

 for(int i=1;i<=N;i++){ 此条语句就是对每一个数进行统计(排序前)
  increase(status[i],1); 如果统计到k,则前k-1个数的分别应该在s中的位置就已经
     确定了,故:
  ans[i] = sum(status[i]-1); 答案就是,比当前数在整个数组中的位置小一的和即s[status[i]-1];
 }

有了以上一个题目作基础,再看次题目是不是有一些眉目了,对了,求三元应该在二元的基础上,以此类推便可以得到五元的
数目了,解释:
 因为每一个数在整个数组中的相对位置是不变的,说以比方说对于数组 4 5 6 1 2 3 7 8,对于6有两个数4和5可以同
 6组成二元组,如果按照统计二元组的方法统计三元组,那么循环统计到7的时候,其前面必然可以找到6,而此时对于
 7,其总量就应该增加在二元组结果中6的答案数,同样应该增加5(1)、3(2)、2(1)的答案数即7的三元组答案是
 1+2+1+2 = 6.以此类推可的到最终答案,不在赘述。
显然使用树状数组的时间复杂度为O(nlgn)


参考程序:(注意要用高精度实现加法)
programme:pku 3378

#include<iostream>
#include<algorithm>

using namespace std;

#define BUFSIZE 60000

#define INT __int64

struct Num{
 int id;
 int key;
};

Num table[BUFSIZE];
int status[BUFSIZE];

int N;

INT S[BUFSIZE];
INT ans[BUFSIZE];
INT temp[BUFSIZE];

struct hp{
 int s[30];
 int n;
};

hp Shp[BUFSIZE];
hp anshp[BUFSIZE];
hp temphp[BUFSIZE];

bool comp(Num a,Num b)
{
 return a.key<b.key;
}

int init()

 for(int i=1;i<=N;i++){
  scanf("%d",&table[i].key);
  table[i].id = i;
 }
 
 sort(table+1,table+N+1,comp);

 int t = 0,p = -1;

 for(int i=1;i<=N;i++){
  
  if(table[i].key>p){
   t++;  
   p = table[i].key;
  }  
  
  status[table[i].id] = t;
 }
}

int lowbit(int x)
{
 return x&(x^(x-1));
}

void increase(int n,INT v)
{
 for(int i=n;i<=N;i+=lowbit(i))
  S[i] += v;
}
  
INT sum(int n)
{
 INT t = 0;
 
 for(int i=n;i>0;i-=lowbit(i))
  t += S[i];
  
 return t;
}   

void add(hp &a,hp b)
{
 int c = 0;
 if(a.n>b.n){
  for(int j=0;j<b.n;j++){
   a.s[j] += c+b.s[j];
   c = a.s[j]/10;
   a.s[j] %= 10;
  }
  for(int j=b.n;j<a.n;j++){
   a.s[j] += c;
   c = a.s[j]/10;
   a.s[j] %= 10;
  }
  if(c>0){
   a.s[a.n] = c;
   a.n++;
  }
 }else {
  for(int j=0;j<a.n;j++){
   a.s[j] += c+b.s[j];
   c = a.s[j]/10;
   a.s[j] %= 10;
  }
  for(int j=a.n;j<b.n;j++){
   a.s[j] = c+b.s[j];
   c = a.s[j]/10;
   a.s[j] %= 10; 
  }
  if(c>0){
   a.s[b.n] = c;
   a.n = b.n+1;
  }else
   a.n = b.n;       
 }
}

void increasehp(int n,hp v)
{
 for(int i=n;i<=N;i+=lowbit(i))
  add(Shp[i],v);
}
   
  
void sumhp(int n,hp &t)
{
 t.n = 1;
 t.s[0] = 0;
  
 for(int i=n;i>0;i-=lowbit(i))
  add(t,Shp[i]);
}   

void copy(hp &a, hp b)
{
 a.n = b.n;
 for(int i=0;i<b.n;i++)
  a.s[i] = b.s[i];
}
   
int main()

 while(scanf("%d",&N)!=EOF){
  
  init();
 
  fill_n(temp,N+1,1);

  for(int k=1;k<3;k++){ 
  
   fill_n(S,N+1,0);
  
   for(int i=1;i<=N;i++){
    increase(status[i],temp[i]);
    ans[i] = sum(status[i]-1);
   }
   for(int i=1;i<=N;i++)
    temp[i] = ans[i];
  }
  
  for(int i=1;i<=N;i++){
   temphp[i].n = 0;
   while(ans[i]!=0){
    temphp[i].s[temphp[i].n++] = ans[i]%10;
    ans[i] /= 10;
   }
  }
     
  for(int k=1;k<3;k++){
   
   for(int i=1;i<=N;i++)
    Shp[i].n = 1,Shp[i].s[0] = 0; 
    
   for(int i=1;i<=N;i++){
    increasehp(status[i],temphp[i]);
    sumhp(status[i]-1,anshp[i]);
   }       
   for(int i=1;i<=N;i++)
    copy(temphp[i],anshp[i]);
  }
  
  hp sum;
  sum.n = 1,sum.s[0] = 0;
  
  for(int i=1;i<=N;i++)
   add(sum,anshp[i]);       

  for(int i=sum.n-1;i>=0;i--)
   printf("%d",sum.s[i]);
  printf("/n");
 }
 return 0;
}

 

[PKU 3378]Crazy Thairs(平衡树)

【题目大意】:N个数(N),求5个互相不逆序的数的组合有多少个。【题目分析】:这个题的重要的地方在于要统计的是长度为5的正序序列的个数,我们就可以通过正序对个数的求法来类比出来正确的方法。正序的求法我...
  • Skyprophet
  • Skyprophet
  • 2009年09月08日 12:32
  • 643

树状数组---原理代码实现

刚刚学了树状数组,有必要总结一下;参考了大牛的博客
  • u011644423
  • u011644423
  • 2014年08月01日 10:21
  • 1652

【专题】树状数组(完整版)

树状数组是对一个数组改变某个元素和求和比较实用的数据结构。两中操作都是O(logn)。  传统数组(共n个元素)的元素修改和连续元素求和的复杂度分别为O(1)和O(n)。树状数组通过将线性结...
  • zhengxu001
  • zhengxu001
  • 2012年09月28日 19:36
  • 3619

树状数组 --区间查询+区间修改

数据结构
  • FSAHFGSADHSAKNDAS
  • FSAHFGSADHSAKNDAS
  • 2016年09月24日 13:45
  • 4184

PKU Campus 2016 I:PKU Zealots(模拟)

PKU Campus 2016 I:PKU Zealots(模拟)
  • PKU_ZZY
  • PKU_ZZY
  • 2017年02月13日 17:48
  • 533

对树状数组的一点理解

树状数组 一、概述 树状数组是一种 用数组进行存储的 自下而上进行操作的  多叉树。 最基本的应用就是维护一个支持两种操作的数列:1.让A[i]加上某数X     2.求一个区间A[L] + A[L+...
  • u012891242
  • u012891242
  • 2015年04月13日 17:01
  • 1338

PKU OJ 类

按照ac的代码长度分类(主要参考最短代码和自己写的代码)   短代码:0.01K--0.50K;中短代码:0.51K--1.00K;中等代码量:1.01K--2.00K;长代码:2.01K以上。  ...
  • baidu_24789559
  • baidu_24789559
  • 2014年12月24日 13:13
  • 615

poj 3378 Crazy Thairs 树状数组+高精度+dp

题意: 给一个长为n的序列,求里面长度为5的上升子序列有多少个。 分析: 树状数组c[i][j]表示以i结尾长度为j的序列数量,要用高精度。 代码: //poj 3378 //sep9 #i...
  • sepNINE
  • sepNINE
  • 2014年12月27日 23:08
  • 550

[树状数组] 区间求和的三种模型

树状数组在区间求和问题上有很高的效率,尤其在非常困难的比赛中(数据量大,对时间限制很严格的比赛)能发挥非常大的作用,其各种复杂度都要比线段树低很多,而且其代码简洁优美……有关区间求和,主要有以下三个模...
  • u012848631
  • u012848631
  • 2015年07月22日 22:06
  • 1706

树状数组实现 区间修改+区间查询

树状数组的本职:单值修改+区间查询 对于区间修改 首先想到的就是线段树 可是线段树的代码太tm长了 是真的懒得写 然后就学习了一下如何用树状数组实现 区间修改+区间查询...
  • MM__1997
  • MM__1997
  • 2017年08月04日 20:17
  • 532
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:pku 3378(树状数组实现)
举报原因:
原因补充:

(最多只允许输入30个字)