树状数组
细节:
在存入树状数组的时候数字最小为1,如果a[i]可以为0,那么a[i]++
在进行add操作时,i < N,这里的N开大,不然会导致tr[i]加的更新的数据失误
注意树状数组的下标是以什么为下标,数组大小不可开错
树状数组操作
- add(x, k)表示将序列中第x个数加上k。
void add(int x, int k)
{
for(int i = x; i <= n; i += lowbit(i))
t[i] += k;
}
以add(3, 5)为例:
在整棵树上维护这个值,需要一层一层向上找到父结点,并将这些结点上的t[x]值都加上k,这样保证计算区间和时的结果正确。时间复杂度为O(logn)
- sum(x)表示将查询序列前x个数的和(类前缀和)
以ask(7)为例:
查询这个点的前缀和,需要从这个点向左上找到上一个结点,将加上其结点的值。向左上找到上一个结点,只需要将下标 x -= lowbit(x),例如 7 - lowbit(7) = 6。
int ask(int x)
{
int sum = 0;
for(int i = x; i; i -= lowbit(i))
sum += t[i];
return sum;
}
题目链接
https://www.acwing.com/problem/content/243/
题目描述
【树状数组】
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1000010;
typedef long long LL;
int tr[N], a[N];
LL res[N];
int n;
//先上模板树状数组
int lowbit(int x) {
return x & -x;
}
void add(int id, int x) {//注意这里是“N”而不是"n",不然会少数据
for(int i = id; i < N; i += lowbit(i)) tr[i] += x;
}
int sum(int x) {
int res = 0;
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main() {
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i], a[i]++;
//记录每个数字对应的逆序数总合
//左边比i大的
for(int i = 1; i <= n; i ++) {
add(a[i], 1);
res[i] = i - sum(a[i]);
}
memset(tr, 0, sizeof tr);
//右边比i小的
for(int i = n; i; i --) {
add(a[i], 1);
res[i] += sum(a[i] - 1);
}
LL ans = 0;
for(int i = 1; i <= n; i ++)
ans += (1 + res[i]) * (LL)res[i] / 2;
cout << ans;
return 0;
}
【暴力代码】
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 200010;
int li[N], ri[N], lx[N], rx[N];
int a[N];
int n;
int main() {
int n;
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++ )
for(int j = 1; j < i; j ++)
if(a[j] < a[i]) li[i] ++;
else if(a[j] > a[i]) lx[i] ++;
for(int i = 1; i <= n; i ++ )
for(int j = n; j > i; j --)
if(a[j] < a[i]) ri[i] ++;
else if(a[j] > a[i]) rx[i] ++;
LL v = 0, M = 0;
for(int i = 1; i <= n; i ++)
v += (LL)li[i] * ri[i], M += (LL)lx[i] * rx[i];
cout << M << " " << v;
return 0;
}
【树状数组代码】
#include <iostream>
#include <cstring>
using namespace std;
const int N = 200010;
typedef long long LL;
int a[N];
int n, m;
LL tr[N]; //树状数组存的一块的和
int li[N], ri[N], lx[N], rx[N];
//上树状数组模板
int lowbit(int x) {
return x & -x;
}
void add(int x, int d) {
for(int i = x; i <= n; i += lowbit(i))
tr[i] += d;
}
LL sum(int x) { //区间前缀和
LL sum = 0;
for(int i = x; i; i -= lowbit(i))
sum += tr[i];
return sum;
}
int main() {
cin >> n;
for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++) {
li[i] = sum(a[i] - 1);
lx[i] = sum(n) - sum(a[i]);
add(a[i], 1);
}
memset(tr, 0, sizeof tr);
for(int i = n; i; i --) {
ri[i] = sum(a[i] - 1);
rx[i] = sum(n) - sum(a[i]);
add(a[i], 1);
}
LL resl = 0, resr = 0;
for(int i = 1; i <= n; i ++) {
resr += li[i] * (LL)ri[i];
resl += lx[i] * (LL)rx[i];
}
cout << resl << " " << resr;
return 0;
}
小朋友排队
题目链接
https://www.acwing.com/problem/content/1217/
题目描述
思路
统计每个数的逆序数和,然后对其做等差数列和相加
【暴力代码】
#include <iostream>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 100010;
LL cnt[N];
int n, h[N];
int main() {
cin >> n;
for(int i = 0; i < n; i++) scanf("%d", &h[i]);
for(int i = 0; i < n; i++) {
int res = 0;
for(int j = 0; j < i; j++)
if(h[j] > h[i])
res++;
for(int j = i + 1; j < n; j++)
if(h[j] < h[i])
res++;
cnt[i] = res;
}
LL ans = 0;
for(int i = 0; i < n; i++) {
ans += (1 + cnt[i]) * cnt[i] / 2;
}
cout << ans;
return 0;
}
【树状数组】
#include <iostream>
#include <cstring>
using namespace std;
//这里N为1e6 + 10,因为树状数组以身高为下标
const int N = 1000010;
typedef long long LL;
int tr[N], a[N];
LL res[N];
int n;
//先上模板树状数组
int lowbit(int x) {
return x & -x;
}
void add(int id, int x) {//注意这里是“N”而不是"n",不然会少数据
for(int i = id; i < N; i += lowbit(i)) tr[i] += x;
}
int sum(int x) {
int res = 0;
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main() {
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i], a[i]++;
//记录每个数字对应的逆序数总合
//左边比i大的
for(int i = 1; i <= n; i ++) {
add(a[i], 1);
res[i] = i - sum(a[i]);
}
memset(tr, 0, sizeof tr);
//右边比i小的
for(int i = n; i; i --) {
add(a[i], 1);
res[i] += sum(a[i] - 1);
}
LL ans = 0;
for(int i = 1; i <= n; i ++)
ans += (1 + res[i]) * (LL)res[i] / 2;
cout << ans;
return 0;
}