树状数组不用多讲了,网上有关于这方面的内容很多,找找都是一大片,而且也不是很难,去学一下就可以很快领悟,至于怎么用,这就需要大量的做题了!
先提个注意点,由于Lowbit(0) = 0,这会导致x递增的那条路径发生死循环,所有当树状数组中可能出现0时,我们都全部加一,这样可以避免0带来的麻烦~~
简单:
POJ 2299 Ultra-QuickSort
http://acm.pku.edu.cn/JudgeOnline/problem?id=2299
求逆序数,可以用经典的归并排序做,也是基本的树状数组题目。
因为a[i]比较大,但只有最多500000个,所以要离散化。
逆序数就是求前面的数比后面的数大的次数。从后往前看就是求后面比前小的数的个数。用树状数组!c[i]表示比i的值小的个数。
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include < algorithm >
using namespace std;
int n;
struct node
{
int num;
int set ;
}a[ 500005 ];
int hash[ 500005 ];
__int64 c[ 500005 ];
int cmp(node b,node c)
{
return c.num > b.num;
}
int Lowbit( int t)
{
return ( - t) & t;
}
__int64 Sum( int end)
{
__int64 sum = 0 ;
while (end > 0 )
{
sum += c[end];
end -= Lowbit(end);
}
return sum;
}
void Modity( int pos, int add)
{
while (pos <= n)
{
c[pos] += add;
pos += Lowbit(pos);
}
}
int main()
{
int i;
while (scanf( " %d " , & n) != EOF && (n != 0 ))
{
for (i = 0 ;i <= n;i ++ )
c[i] = 0 ;
for (i = 1 ;i <= n;i ++ )
{
scanf( " %d " , & a[i].num);
a[i]. set = i;
}
sort(a + 1 ,a + 1 + n,cmp);
for (i = 1 ;i <= n;i ++ )
{
hash[a[i]. set ] = i;
}
__int64 sum = 0 ;
for (i = n;i >= 1 ;i -- )
{
sum += Sum(hash[i]);
Modity(hash[i], 1 );
}
printf( " %I64d\n " ,sum);
}
return 0 ;
}
POJ 2352 Stars
http://acm.pku.edu.cn/JudgeOnline/problem?id=2352
题目意思就是求每个星星左下方的星星的个数,由于y轴已经排序好了,我们可以直接用按x轴建立一维树状数组,然后求相当于它前面比它小的个数,模板直接一套就搞定了~~
POJ 1195 Mobile phones
http://acm.pku.edu.cn/JudgeOnline/problem?id=1195
二维的树状数组,直接把Update()和Getsum()改为二维即可。
如Update()函数改为:
void Update(int x, int y, int d)
{ // 注意:当i = 0,0 + Lowbit(0) = 0,会造成死循环!
int i, j;
for (i = x; i < maxn; i += Lowbit(i)) // 注意这里是maxn,是tree[]的大小.
{
for (j = y; j < maxn; j += Lowbit(j))
{
tree[i][j] += d;
}
}
}
写这题的好时候就是求区间和时应该是(x2,y2)-(x2,y1-1)-(x1-1,y2)+(x1,y1)。而不是(x2,y2)-(x2,y1)-(x1,y2)+(x1,y1).
POJ 2481 Cows
http://acm.pku.edu.cn/JudgeOnline/problem?id=2481
将E从打到小排序,如果E相等按S排序,然后就跟POJ 2352 Stars做法一样了~~
POJ 3067 Japan
http://acm.pku.edu.cn/JudgeOnline/problem?id=3067
先按第一个坐标排序从大到小排序,如果相等按第二个坐标从大到小排序,然后就又是跟Cows和Stars做法相同了...
做的时候有两点注意:1,算逆序的时候相同的不应该算,即求和时应该减一。
2,要用__int64
POJ 2029 Get Many Persimmon Trees
http://acm.pku.edu.cn/JudgeOnline/problem?id=2029
O(n ^ 2)枚举起点,再用二维树状数组求其中的点数即可。
中等:
POJ 2155 Matrix
http://acm.pku.edu.cn/JudgeOnline/problem?id=2155
经典树状数组题目,分析见前一篇文章(树状数组学习系列1 之 初步分析——czyuan原创)~~
POJ 3321 Apple Tree
http://acm.pku.edu.cn/JudgeOnline/problem?id=3321
这题的难点不在于树状数组,而是如果将整棵树映射到数组中。我们可以用DFS()改时间戳的方法,用begin[i]表示以i为根的子树遍历的第一个点,end[i]表示以i为根的子树遍历的最后一个点。
比如数据为:
5
1 2
2 5
2 4
1 3
那么begin[] = {1, 2, 5, 4, 3}, end[] = {5, 4, 5, 4, 3},下标从1开始。
对于每个点都对应一个区间(begin[i], end[i]),如果要改变点a的状态,只要Update(begin[a]),要求该子树的苹果树,即Getsum(begin[a] ) - Getsum(end[a] + 1),(注:这里求和是求x递增的路径的和。)
POJ 1990 MooFest
http://acm.pku.edu.cn/JudgeOnline/problem?id=1990
这题的难点是要用两个一维的树状数组,分别记录在它前面横坐标比它小的牛的个数,和在它前面横坐标比它小的牛的横坐标之和。
按音量排个序,那么式子为:
ans += 1LL * cow[i].volumn * (count * x - pre + total - pre - (i - count) * x);
cow[i].volumn为该牛的能够听到的音量。
count为在第i只牛前面横坐标比它小的牛的个数。
pre为在第i只牛前面横坐标比它小的牛的横坐标之和。
total 表示前i - 1个点的x坐标之和。
分为横坐标比它小和横坐标比它大的两部分计算即可。
Hdu 3015 Disharmony Trees
http://acm.hdu.edu.cn/showproblem.php?pid=3015
跟上题方法相同,只要按它的要求离散化后,按高度降序排序,套用上题二个树状数组的方法即可。
Hdu 2852 KiKi's K-Number
http://acm.hdu.edu.cn/showproblem.php?pid=2852
这题与上面那题类似,只是要求比a大的第k大的数,那我们用Getsum(a)求出小于等于a的个数,那么就是要我们求第k + Getsum(a)大的数,而删除操作只要判断Getsum(a) – Getsum(a - 1)是否为0,为0则说明a不存在。
难题:
POJ 2464 Brownie Points II
http://acm.pku.edu.cn/JudgeOnline/problem?id=2464
这道题用二分也可以做的,这里介绍下树状数组的做法。首先有n个点,过每个点可以做x,y轴,把平面切成BL, TL, TR, BR四个部分,我们现在的问题是如果快速的计算这四个部分的点的个数。
这样我们可以先预处理,先按y坐标排序,求出每个点正左方和正右方的点的个数LeftPoint[], RightPoint[],复杂度为O(n),同样我们再以x坐标排序,求出每个点正上方和正下方点的个数UpPoint[], DownPoint[]。还要求出比点i y坐标大的点的个数 LageY[]。注意:这里要进行下标映射,因为两次排序点的下标是不相同的。
然后按x坐标从小到大排序,x坐标相等则y坐标从小到大排序。我们可以把y坐标放在一个树状数组中。
对于第i个点,求出Getsum(y[i])即为BL的个数,然后Update(y[i])。由于现在是第i点,说明前面有i – 1个点, 那么
TL = i - 1 - LeftPoint[i]- BL;
TR = LargeY[i] - TL – UpPoint[i] ;
BR = n - BL - TL - TR - LeftPoint[i] - RightPont[i] - UpPoint[i] – DownPont[i] - 1;
这样我们就求出四个部分的点的个数,然后判断有没有当前解优,有的话就更新即可~~
![ContractedBlock.gif](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include < algorithm >
using namespace std;
#define maxn 200005
struct node
{
int x;
int y;
int LeftPoint; // 正左边的点数
int RightPoint; // 正右边的点数
int UpPoint; // 正上方的点数
int DownPoint; // 正下方的点数
int LargeY; // 比该点Y大的数的数目
}p[maxn];
int C[maxn];
int BL,TL,TR,BR;
/* 对第j个点的四块内的点数
BL = Getsum(p[j].y) - p[j].LeftPoint - p[j].DownPoint;
TL = j - p[j].LeftPoint - p[j].DownPoint - BL;
TR = p[j].LargeY - TL - p[j].UpPoint;
BR = n - j - 1 - TR - p[j].RightPoint - p[j].UpPoint;
*/
struct node1
{
int Stan;
int Ollie;
}V[maxn];
bool cmp(node a,node b)
{
if (a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
bool cmp2(node a,node b)
{
if (a.y == b.y)
return a.x < b.x;
return a.y < b.y;
}
bool cmp3(node1 a,node1 b)
{
if (a.Stan == b.Stan)
return a.Ollie < b.Ollie;
return a.Stan > b.Stan;
}
// bool cmp1(node a,node b){return a.x < b.x;}
int lowb( int t)
{
return t & ( - t);
}
void Updata( int i)
{
while (i < maxn)
{
C[i] += 1 ;
i += lowb(i);
}
}
int Getsum( int i)
{
int s = 0 ;
while (i > 0 )
{
s += C[i];
i -= lowb(i);
}
return s;
}
int main()
{
int i,j,k;
int n,num;
while (scanf( " %d " , & n) != EOF && n != 0 )
{
memset(C, 0 , sizeof (C));
for (i = 0 ;i < n;i ++ )
{
scanf( " %d%d " , & p[i].x, & p[i].y);
}
sort(p,p + n,cmp); // X轴离散化
for (num = 1 ,i = 0 ;i < n;num ++ )
{
for (j = i + 1 ;j < n;j ++ )
{
if (p[i].x != p[j].x)
break ;
}
for (k = i;k < j;k ++ )
{
p[k].UpPoint = j - 1 - k;
p[k].DownPoint = k - i;
p[k].x = num;
}
i = j;
}
sort(p,p + n,cmp2); // Y轴离散化
for (num = 1 ,i = 0 ;i < n;num ++ )
{
for (j = i + 1 ;j < n;j ++ )
{
if (p[i].y != p[j].y)
break ;
}
for (k = i;k < j;k ++ )
{
p[k].LeftPoint = k - i;
p[k].RightPoint = j - 1 - k;
p[k].y = num;
p[k].LargeY = n - j;
}
i = j;
}
sort(p,p + n,cmp);
for (num = 0 ,i = 0 ;i < n;num ++ )
{
int Stan_num = 0x7fffffff ;
int Ollie_num = 0 ;
for (j = i;j < n;j ++ )
{
if (p[j].x != p[i].x)
break ;
BL = Getsum(p[j].y) - p[j].LeftPoint - p[j].DownPoint;
TL = j - p[j].LeftPoint - p[j].DownPoint - BL;
TR = p[j].LargeY - TL - p[j].UpPoint;
BR = n - j - 1 - TR - p[j].RightPoint - p[j].UpPoint;
if (TL + BR > Ollie_num)
{
Stan_num = BL + TR;
Ollie_num = TL + BR;
}
else if (TL + BR == Ollie_num)
{
if (BL + TR <= Stan_num)
{
Stan_num = BL + TR;
}
}
Updata(p[j].y);
}
V[num].Ollie = Ollie_num;
V[num].Stan = Stan_num;
i = j;
}
sort(V,V + num,cmp3);
printf( " Stan: %d; Ollie: " ,V[ 0 ].Stan);
for (i = 0 ;i < num;i ++ )
{
if (V[i].Stan != V[ 0 ].Stan)
break ;
if (V[i].Ollie == V[i - 1 ].Ollie && i != 0 )
continue ;
printf( " %d " ,V[i].Ollie);
}
printf( " ;\n " );
}
// system("pause");
return 0 ;
}