刷题笔记-树状数组
动态求连续区间和 -模板题
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 100010;
int s[N];
int w[N];
int n , m;
int lowbit(int x)
{
return x & -x;
}
int getSum(int x)
{
int res = 0;
for(int i = x; i > 0; i -= lowbit(i))
res += s[i];
return res;
}
void add(int x,int a)
{
for(int i = x; i <= n; i += lowbit(i))
s[i] += a;
}
int main(void)
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
scanf("%d", &w[i]);
add(i , w[i]);
}
int k ,a ,b;
while(m -- )
{
scanf("%d%d%d",&k , &a , &b);
if(k == 0) printf("%d\n",getSum(b) - getSum(a - 1));
else add(a , b);
}
return 0;
}
数星星
思路:
树状数组用来保存每个x坐标上星星的总数,那么每一颗新星(x,y)的等级为getSum(x)
然后再将这一颗新星加入树状数组add(x,1);
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 32010;
int s[N];
int ans[N];
int lowbit(int x)
{
return x & -x;
}
int getSum(int x)
{
int res = 0;
for(int i = x; i > 0; i -= lowbit(i))
res += s[i];
return res;
}
void add(int x,int a)
{
for(int i = x; i <= 32001; i += lowbit(i))
s[i] += a;
}
int main(void)
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
int x,y;
cin >> x >> y; x++;
int tmp = getSum(x);
ans[tmp]++;
add(x,1);
}
for(int i = 0; i < n; i++)
cout << ans[i] << endl;
return 0;
}
小朋友排队
思路:
1.最开始想这是一个排序题,和逆序对的数量那一题类似,实际上是一个排序背景下求动态前缀和的问题。
2.假设序列中逆序对的个数是K,那么交换相邻两个元素最多会使逆序对的数量减少1,因为这种交换只会影响这两个元素,它们相对其他元素的位置相当于没有变化。
- 说明至少交换K次,才能消除所有的逆序对
3.冒泡排序是一种交换相邻两个元素的排序,且每一次交换必然使逆序对的个数减一,所以采用冒泡的方式进行排序,可以只交换K次,就能消除所有逆序对 - 说明最优方案一定是交换了K次,且交换的规则类似于冒泡排序
4.在冒泡排序中,考虑一个一般元素,发现它的交换次数:在它前面且比它大的元素个数 + 在它后面且比它小的元素个数、 - 所以本质上,是要我们分别求序列的动态前向&后向前缀和
5.使用归并排序也可以做,只是难在怎样记录每一个元素的交换次数,因为在排序中一个元素的位置是一直在变化的,所以不能用下标,且元素的值不唯一,也不能使用值作为下标。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1000010;
int a[N],s[N],val[N];
int n;
LL ans;
int lowbit(int x)
{
return x & -x;
}
void add (int x,int a)
{
for(int i = x; i < N; i += lowbit(i))
s[i] += a;
}
int getSum(int x)
{
int res = 0;
for(int i = x; i > 0; i -= lowbit(i))
res += s[i];
return res;
}
int main(void)
{
cin >> n;
for(int i = 0; i < n; i++)
{
scanf("%d",&a[i]);
a[i]++;
//求在第i个元素之前,且比它大的元素个数
add(a[i], 1);
val[i] = getSum(N - 1) - getSum(a[i]);
}
memset(s,0,sizeof(s));
for(int i = n-1; i >= 0; i--)
{
//求在第i个元素之后,且比它小的元素个数
add(a[i], 1);
val[i] += getSum(a[i] - 1);
}
for(int i = 0; i < n; i++)
ans += (LL)(1 + val[i])* val[i] / 2;
cout << ans << endl;
return 0;
}