数据结构·树状数组
时过两年,我!终于理解树状数组了!
在这贴上让我恍然大明白的b站视频链接
树状数组中的小问题
单点修改的时候注意d不要为0
不然就会出现加的lowbit一直为0导致死循环的情况
(sxht dalao居然一言道出问题所在 %%%)
树状数组
树状数组可以高效地完成单点查询和区间修改。
剩下的有时间补,贴上修改和查询的代码↓
int lowbit(int x) { return x&-x; }
void _update(int d,int x)
{
while (d<=N) t[d]+=x,d+=lowbit(d);
}
int _query(int x)
{
int res=0;
while (x)
{
res+=t[x];
x-=lowbit(x);
}
return res;
}
Ehhh Ah
【YbtOj】例题
A.单点修改区间查询
树状数组板子题(逃
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,q;
int a[N];
int t[N];
int lowbit(int x) { return x&-x; }
void _update(int d,int x)
{
while (d<=N)
{
t[d]+=x;
d+=lowbit(d);
}
}
int _query(int x)
{
int res=0;
while (x)
{
res+=t[x];
x-=lowbit(x);
}
return res;
}
signed main()
{
scanf("%lld%lld",&n,&q);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
_update(i,a[i]);
}
while (q--)
{
int op,x,y;
scanf("%lld%lld%lld",&op,&x,&y);
if (op==1) _update(x,y);
else printf("%lld\n",_query(y)-_query(x-1));
}
return 0;
}
B.逆序对
显然,以\(a_{i}\)为第一个元素的逆序对的数量就是 \(j\in [i+1,n],a_{i}>a_{j}\) 的数量,所以只需要统计出每个 \(i\) 后值比 \(a_{i}\) 小的 \(j\) 的数量,它们的和就是最终答案。
于是乎,这就相当于是一个“权值树状数组”,每次查询比 \(a_{i}\) 小的值的数量,注意倒序扫描。因为值域较大,所以先离散化就好了
贴
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+5;
int n;
struct node{
int x,d;
}a[N];
int rk[N];
int t[N];
int cnt;
int lowbit (int x) { return x&-x; }
bool cmp(node x,node y)
{
if (x.x==y.x) return x.d<y.d;
return x.x<y.x;
}
void _update(int d,int x)
{
while (d<=N)
{
t[d]+=x;
d+=lowbit(d);
}
}
int _sum(int x)
{
int res=0;
while (x)
{
res+=t[x];
x-=lowbit(x);
}
return res;
}
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i].x);
a[i].d=i;
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++) rk[a[i].d]=i;//离散化
for (int i=n;i>=1;i--)
{
_update(rk[i],1);
cnt+=_sum(rk[i]-1);
}
printf("%lld",cnt);
return 0;
}
C.严格上升子序列数
需要小小地dp一下
设状态 \(f_{i,a_{j}}\) 表示长度为 \(i\) ,以 \(a_{j}\) 结尾的严格上升子序列的个数。这样显然,状态转移方程为
\[f_{i,a_{j}}=\sum _{k=1,a_{k}<a_{j}} ^{j-1}{f_{i-1,k}}
\]
这里可以用类似于树状数组维护逆序对的方法维护\(\sum _{k=1,a_{k}<a_{j}} ^{j-1}{f_{i-1,k}}\) 。
再次注意到较大的值域,所以还要先离散化
贴
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
const int MOD=1e9+7;
int T;
int n,m;
struct node{
int x,d;
}a[N];
int rd[N];
int t[N];
int f[N][N];
int cnt;
bool cmp (node x,node y)
{
if (x.x==y.x) return x.d<y.d;
return x.x<y.x;
}
int lowbit(int x) { return x&-x; }
void _update(int d,int x)
{
while (d<=N)
{
t[d]+=x;
d+=lowbit(d);
}
}
int _sum(int x)
{
int res=0;
while (x)
{
res+=t[x];
x-=lowbit(x);
}
return res;
}
signed main()
{
scanf("%lld",&T);
for (int k=1;k<=T;k++)
{
cnt=0;
memset(f,0,sizeof f);
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i].x);
a[i].d=i;
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++) rd[a[i].d]=i;//离散化
for (int i=1;i<=n;i++) f[1][i]=1;//边界
rd[0]=1;//还是边界
for (int i=2;i<=m;i++)
{
memset(t,0,sizeof t);
for (int j=1;j<=n;j++)
{
f[i][j]=(f[i][j]+_sum(rd[j]-1))%MOD;
_update(rd[j],f[i-1][j]);
}
}
for (int i=1;i<=n;i++) cnt=(cnt+f[m][i])%MOD;
printf("Case #%lld: %lld\n",k,cnt);
}
return 0;
}
G.星星问题
这题的输入非常的友好,已经是有序了。那么直接二维数点就行了。(或者叫,超级简化版的二维数点?)
每输入一个点就按照 \(x\) 坐标查询一下,然后再把 \(x\) 放进去即可。因为给出的 \(y\) 有序,就不用考虑 \(y\) 了(懂事的输入没糖吃)
贴
#incIude <bits/stdc++.h>
using namespace std;
const int N=15005;
const int M=32005;
int n;
int tr[M];
int ans[N];
int lowbit(int x) { return x&-x; }
inline void add(int d,int x)
{
while (d<=M) tr[d]+=x,d+=lowbit(d);
}
inline int sum(int x)
{
int res=0;
while (x)
{
res+=tr[x];
x-=lowbit(x);
}
return res;
}
int main()
{
scanf("%d",&n);
for (int i=1,x,y;i<=n;i++)
{
scanf("%d%d",&x,&y);
x++;
ans[sum(x)]++;
add(x,1);
}
for (int i=0;i<=n-1;i++) printf("%d\n",ans[i]);
return 0;
}
H.维护差数
在此批判YbtOj,并没有说 \(i<j\) 的条件(我很恼火因为我刚开始排序了)
给式子进行一个简单的移项,就变成了满足 \(a_{i}-i<a_{j}-j\ ,\ i<j\) 的数会产生贡献。于是套个板子就出来了。
注意\(a_{i}-i\)可能会小于\(0\),所以还要加上一个偏移量。
贴
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
const int MOD=12345;
int n;
int a[N];
int tr[N<<1];
int ans;
int lowbit(int x) { return x&-x; }
void add(int d,int x)
{
while (d<(N<<1)) tr[d]+=x,d+=lowbit(d);
}
int sum(int x)
{
int res=0;
while (x)
{
res+=tr[x];
x-=lowbit(x);
}
return res;
}
signed main()
{
scanf("%lld",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]=a[i]-i+N;
}
for (int i=1;i<=n;i++)
{
add(a[i],1);
ans=(ans+sum(a[i]-1))%MOD;
}
printf("%lld",ans);
return 0;
}