回顾lowbit
lowbit运算定义为整数n在二进制下,最低位的以及后面所有的0构成的数值
lowbit(x)是用来跳到x的下一个结点或者上一个结点的的介质
lowbit(n)=n&(~n+1)=n&(-1-n+1)=n&(-n)
这段代码可以求出x在二进制下为1的位数
scanf("%lld", &x);
for (int i = 0; i <= 20; i++)
H[1 << i] = i;//预处理一个像hash表一样的东西
while (x > 0)
{
printf("%lld ", H[x & -x]);
x -= (x & -x);
}
这段代码可以求出区间[1,x]划分的log(x)个小区间
scanf("%lld", &x);
while (x > 0)
{
printf("[%lld %lld]\n", x - (x & -x) + 1, x);
x -= (x & -x);
}
树状数组
c[i]保存的是区间[x-lowbit(x)+1,x]中所有数的和
性质
- 每个节点c[x]保存以它为根的子树中所有叶节点的和
- 树的深度为O(log N)
- 每个内部节点c[x]的子节点个数为lowbit(x)在二进制下的的位数
- 除树根外,每个内部节点c[x]的父亲节点是c[x+lowbit(x)]
基本操作
树结点的序号都是偶数,奇数的叶子结点都是一个单独的区间
ll lowbit(ll x) { return x & -x; }//定义lowbit运算
初始化
void init()//初始化,将原始序列a数组构造成树状数组
{
for (int i = 1; i <= n; i++)
add(i, a[i]);
}
区间查询
ll ask(ll x)//区间查询
{
ll res = 0;
for (; x; x -= lowbit(x))
res += c[x];
return res;
}
单点更新
void add(ll x, ll y)//单点增加
{
for (; x <= N; x += lowbit(x))
c[x] += y;
}
树状数组与逆序对
牛客一个小栗子楼兰图腾
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 200000 + 1100;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, res1, res2;
ll a[N], t[N], r1[N], l1[N], r2[N], l2[N];
ll lowbit(ll x) { return x & -x; }
ll query(ll x)
{
ll res = 0;
for (; x; x -= lowbit(x))
res += t[x];
return res;
}
void add(ll x, ll y)
{
for (x; x <= n; x += lowbit(x))
t[x] += y;
}
int main()
{
scanf("%lld", &n);
memset(t, 0, sizeof(t));
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
//右边
for (int i = n; i >= 1; i--)
{
r1[i] = query(n) - query(a[i]); //V
r2[i] = query(a[i] - 1); //^
add(a[i], 1);
}
//左边
memset(t, 0, sizeof(t));
for (int i = 1; i <= n; i++)
{
l1[i] = query(n) - query(a[i]); //V
l2[i] = query(a[i] - 1); //^
add(a[i], 1);
}
for (int i = 1; i <= n; i++)
{
res1 += (r1[i] * l1[i]);
res2 += (r2[i] * l2[i]);
}
printf("%lld %lld\n", res1, res2);
return 0;
}
树状数组的扩展应用
牛客一道小栗子A Tiny Problem with intergers
TLE代码,过80
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 200000 + 1100;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, m, x, l, r;
ll a[N], ca[N];
int main()
{
// IOS;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
ca[i] = a[i] - a[i - 1];
}
while (m--)
{
char c;
scanf(" %c", &c); //scanf是从标准缓冲区中读取输入的字符的;空格就可以抵消那个回车;或者写两个getchar()
// fflush(stdin); //语句来清空缓冲区
if (c == 'Q')
{
scanf("%lld", &x);
printf("%lld\n", a[x]);
}
else if (c == 'C')
{
scanf("%lld%lld%lld", &l, &r, &x);
ca[l] += x, ca[r + 1] -= x;//影响在l处产生,在r+1处消除
for (int i = 1; i <= n; i++)
a[i] = a[i - 1] + ca[i];
}
}
return 0;
}
AC代码
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 200000 + 1100;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, m, x, l, r;
ll a[N], ca[N];
ll lowbit(ll x) { return x & -x; }
void add(ll x, ll y)
{
for (x; x <= n; x += lowbit(x))
ca[x] += y;
}
ll ask(ll x)
{
ll res = 0;
for (; x; x -= lowbit(x))
res += ca[x];
return res;
}
int main()
{
// IOS;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
add(i, a[i] - a[i - 1]);//树状数组只是用来维护差值
}
while (m--)
{
char c;
scanf(" %c", &c); //scanf是从标准缓冲区中读取输入的字符的;空格就可以抵消那个回车;或者写两个getchar()
if (c == 'Q')
{
scanf("%lld", &x);
printf("%lld\n", ask(x));//查询单点的值对应的是a数组的差分数组的前缀和
}
else if (c == 'C')
{
scanf("%lld%lld%lld", &l, &r, &x);
add(l, x), add(r + 1, -x); //影响在l处产生,在r+1处消除
}
}
return 0;
}
二维树状数组
牛客一道小栗子A Simple Problem with Integers
思想:分离包含有多个变量的项,使公式中不同变量之间相互独立
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 100000 + 1100;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, q, l, r, d;
ll a[N], t[2][N], sum[N];
ll lowbit(ll x) { return x & -x; }
void add(ll k, ll x, ll y)//二维树状数组记录的还是变化量而已【差分】
{
for (x; x <= n; x += lowbit(x))
t[k][x] += y;
}
ll query(ll k, ll x)//查询的是差分的前缀和,也即原数组的变化量
{
ll res = 0;
for (; x; x -= lowbit(x))
res += t[k][x];
return res;
}
int main()
{
// IOS;
scanf("%lld%lld", &n, &q);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
sum[i] = sum[i - 1] + a[i];
}
while (q--)
{
char c;
scanf(" %c", &c);
if (c == 'Q')
{
scanf("%lld%lld", &l, &r);
ll a = sum[r] + (r + 1) * query(0, r) - query(1, r);
ll b = sum[l - 1] + l * query(0, l - 1) - query(1, l - 1);
printf("%lld\n", a - b);
}
else if (c == 'C')
{
scanf("%lld%lld%lld", &l, &r, &d);
add(0, l, d), add(0, r + 1, -d), add(1, l, l * d), add(1, r + 1, -(r + 1) * d);
}
}
return 0;
}
牛客一道小栗子Lost Cows
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
const int N = 1e4;
const int M = 2e6 + 1000;
const int inf = 0x3f3f3f3f;
const int mod = (1 << 31) - 1;
#define IOS \
ios::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
ll n, lim;
ll a[N], t[N], ans[N], p[N];
ll lowbit(ll x) { return x & -x; }
void add(ll x, ll val)
{
for (x; x <= n; x += lowbit(x))
t[x] += val;
}
ll ask(ll x)
{
ll res = 0;
for (; x; x -= lowbit(x))
res += t[x];
return res;
}
ll er_query(ll x)
{
ll l = 1, r = n, mid;
while (l < r)
{
mid = (l + r) >> 1;
if (ask(mid) >= x) //比较的是前缀和与x
r = mid;
else
l = mid + 1;
}
return l;
}
void init()
{
p[0] = 1;
for (int i = 1; i < 20; i++)
p[i] = 1 << i;
lim = log(n) / log(2) + 1; //向上取整
}
ll query(ll x)
{
ll val = 0, step = 0;
for (int i = lim; i >= 0; i--) //枚举p[i]
if (step + p[i] <= n && val + t[step + p[i]] < x)//倍增的思想
{//因为已经预处理过p[i]是2的次幂,所以说t[step+p[i]]是step+p[i]的前缀和,eg:1 2 4 8 16,注意到p[i]最小可以取到1
val += t[step + p[i]];
step += p[i];
}
return step + 1; //***步长永远是趋于x,不可能>x,所以最后要step+1
}
int main()
{
// IOS;
while (~scanf("%lld", &n))
{
memset(t, 0, sizeof(t));
add(1, 1); //
for (int i = 2; i <= n; i++) //从2开始
{
scanf("%lld", &a[i]);
add(i, 1); //
}
init();
for (int i = n; i >= 1; i--) //1~n
{
ll pos = query(a[i] + 1); //法2树状数组+倍增查询
// ll pos = er_query(a[i] + 1);法1:二分查询
add(pos, -1); //查询到的位置上-1,即将1还原为0
ans[i] = pos; //位置pos也即对应的答案【高度】
}
for (int i = 1; i <= n; i++)
printf("%lld\n", ans[i]);
}
return 0;
}