您的位置:首页 > 其它

树状数组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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: