树状数组POJ3067
2013-09-03 22:07
218 查看
#include<iostream> #include<algorithm> #define M 2005 using namespace std; long long tree[M];//tree[]里面的为部分和,也会达到long long级别 long long ans;//否则runtime error //有重边的情况下交点最多可能达到O((k/2)^2)级别 struct edge { int left,right; }road[M*M]; bool cmp(edge a,edge b) { if(a.left==b.left) return a.right<b.right; else return a.left<b.left; } int lowbit(int t)//计算t的最右边的非零位,如6(00110)返回2(00010) { return t&(-t); } void insert(int k,int val,int maxVal)//对位置k上插入一个val { while(k<=maxVal) { tree[k]=tree[k]+val; k=k+lowbit(k); } } long long sum(int k)//求下标k处的部分和 { long long sum=0; while(k>0) { sum=sum+tree[k]; k=k-lowbit(k); } return sum; } int main() { int T; //cin>>T;//超时!!!!!!!!!!!!!!! scanf("%d",&T); for(int tt=1;tt<=T;tt++) { int n,m,k; int maxVal=0; //cin>>n>>m>>k; scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=k;i++) { scanf("%d%d",&road[i].left,&road[i].right); //cin>>road[i].left>>road[i].right; if(road[i].right>maxVal) maxVal=road[i].right; } sort(road+1,road+k+1,cmp); memset(tree, 0, sizeof(tree));//!!!!!!!! ans=0; for(int i=1;i<=k;i++) { ans=ans+(sum(maxVal)-sum(road[i].right));//插入时,当前总节点数为sum(maxVal),因为每次插入时都会向上更新到tree[maxVal] insert(road[i].right,1,maxVal); } //cout<<"Test case "<<tt<<": "<<ans<<endl; printf("Test case %d: %I64d\n",tt,ans); } return 0; }
树状数组,又称二进制索引树,英文名Binary Indexed Tree。一、树状数组的用途主要用来求解数列的前缀和,a[0]+a[1]+...+a。由此引申出三类比较常见问题:1、单点更新,区间求值。(HDU1166)2、区间更新,单点求值。(HDU1556)3、求逆序对。(HDU2838) 假如我现在有个需求,就是要频繁的求数组的前n项和,并且存在着数组中某些数字的频繁修改,那么我们该如何实现这样的需求? ① 传统方法:根据索引修改为O(1),但是求前n项和为O(n)。②空间换时间方法:我开一个数组sum[],sum[i]=a[1]+....+a[i],那么有点意思,求n项和为O(1),但是修改却成了O(N),这是因为我的Sum[i]中牵涉的数据太多了,那么问题来了,我能不能在相应的sum[i]中只保存某些a[i]的值呢?好吧,下面我们看张图。从图中我们可以看到S[]的分布变成了一颗树,有意思吧,下面我们看看S[i]中到底存放着哪些a[i]的值。S[1]=a[1];S[2]=a[1]+a[2];S[3]=a[3];S[4]=a[1]+a[2]+a[3]+a[4];S[5]=a[5];S[6]=a[5]+a[6];S[7]=a[7];S[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];之所以采用这样的分布方式,是因为我们使用的是这样的一个公式:S[i]=a[i-2k+1]+....+a[i]。其中:2k 中的k表示当前S[i]在树中的层数,它的值就是i的二进制中末尾连续0的个数,也是(最低位1的位置,最低位序号为0的话),2k也就是表示S[i]中包含了几个a[],举个例子: i=610=01102 ;可以发现末尾连续的0有一个,即k=1,则说明S[6]是在树中的第二层,并且S[6]中有21项,(同理,8->0100的最低位1的位置为2,S[8]在第四层,S[8]中为4个S[]的和)随后我们求出了起始项: a[6-21+1]=a[5],但是在编码中求出k的值还是有点麻烦的,所以我们采用更灵巧的Lowbit技术,即:2k=i&-i 。 则:S[6]=a[6-21+1]=a[6-(6&-6)+1]=a[5]+a[6]。2:求前n项和 比如上图中,如何求Sum(6),很显然Sum(6)=S4+S6,那么如何寻找S4呢?即找到6以前的所有最大子树,很显然这个求和的复杂度为logN。3:修改如上图中,如果我修改了a[5]的值,那么包含a[5]的S[5],S[6],S[8]的区间值都需要同步修改,我们看到只要沿着S[5]一直回溯到根即可,同样修改的时间复杂度也为logN。
相关文章推荐
- 树状数组与逆序数——树状数组实践POJ3067
- POJ3067(树状数组逆序对)
- POJ3067 Japan(树状数组,逆序数)
- POJ3067 Japan(树状数组)
- poj3067(树状数组求逆序数)
- POJ3067(树状数组:统计数字出现个数)
- 树状数组专题(四)POJ3067
- poj3067 Japan(树状数组)
- 【树状数组】Japan(POJ3067)PASCAL…
- POJ3067 Japan【树状数组】【逆序数】
- POJ3067(树状数组||线段树)
- POJ3067 Japan(树状数组)
- POJ3067:Japan(树状数组求逆序对)
- POJ3067--Japan(树状数组)
- poj3067 Japan 树状数组求逆序对
- 【树状数组--求逆序数】poj3067 Japan
- POJ3067:Japan(树状数组求逆序对)
- poj3067 树状数组求逆序数
- POJ3067 树状数组 情景转化为区间求和
- poj3067 Japan (树状数组)