【Ybt OJ】[数据结构 第2章] 树状数组 [前半章]

45 篇文章 0 订阅
15 篇文章 0 订阅
本文介绍了树状数组在解决单点修改和区间查询问题中的应用,通过实例展示了如何利用树状数组进行区间和的快速更新和查询。同时,讲解了树状数组如何结合离散化解决逆序对问题,以及如何利用树状数组计算严格上升子序列的数量。每个部分都提供了详细的代码实现和分析。
摘要由CSDN通过智能技术生成

「 「 数据结构 」 」 2 2 2章 树状数组 ( ( ( 3 3 3 ) ) )
目录:

A.单点修改区间查询
B.逆序对
C.严格上升子序列数

A . A. A. 例题 1 1 1 单点修改区间查询

在这里插入图片描述
在这里插入图片描述
洛谷 l i n k link link

分析:

树状数组模板题 区间和就 前缀和相减

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e6+5;
int n,m,x,y,z;
ll a[N];
int lowbit(int x){return x&(-x);}
void add(int x,ll k){
	for(;x<=n;x+=lowbit(x)) 
		a[x]+=k;
}
ll ques(int x){
	ll ans=0;
	for(;x;x-=lowbit(x))
		ans+=a[x];
	return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
    	int x;
    	scanf("%d",&x);
    	add(i,1ll*x);
    }
    for(int i=1;i<=m;i++)
    {
    	scanf("%d%d%d",&x,&y,&z);
    	if(x==1) add(y,1ll*z);
    	if(x==2) printf("%lld\n",ques(z)-ques(y-1));
    }
	return 0;
}

B . B. B. 例题 2 2 2 逆序对

在这里插入图片描述
洛谷 l i n k 1 link1 link1 洛谷 l i n k 2 link2 link2

分析:

可用归并排序 但这是树状数组章节
首先要离散化 得出在数组中第几大 然后
树状数组前缀和 那你每次 + 1 +1 +1 不就得出 i i i前面有几个数比 i i i小 他问你有几个比 i i i大的
那就用 i i i个数 − - i i i小的 = = = i i i大的 也就是逆序对个数了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e5+5;
int tree[N*5],awa[N*5],n;
ll ans;
int lowbit(int x){return x&(-x);}
struct node{
	int val,id;
}a[N*5];
bool cmp(node x,node y){
	if(x.val==y.val)
		return x.id<y.id;
	return x.val<y.val;
}
void ins(int p,int x)
{
	for(;p<=n;p+=lowbit(p))
		tree[p]+=x;
}
int query(int p)
{
	int res=0;
	for(;p;p-=lowbit(p))
		res+=tree[p];
	return res;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    	scanf("%d",&a[i].val);
    	a[i].id=i;
	}
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
		awa[a[i].id]=i;  //离散化
    for(int i=1;i<=n;i++)
    {
    	ins(awa[i],1);
    	ans+=i-query(awa[i]);  //当前数量-比i小的
	}
	printf("%lld",ans);
    
	return 0;
}

C . C. C. 例题 3 3 3 严格上升子序列数

在这里插入图片描述
洛谷 l i n k link link

分析:

先弄个 n 3 d p : f i , j n^3dp:f_{i,j} n3dp:fi,j表示 i i i结尾长度为 j j j严格上升子序列数
f i , j = ∑ ( a k < a i , k < i ) ∗ f k , j − 1 f_{i,j}=∑(a_k<a_i,k<i)*f_{k,j-1} fi,j=(ak<ai,k<i)fk,j1
然后先离散化 这个 k k k就可以树状数组处理了 就是个顺序对
以减法代替 m o d mod mod会快很多

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
#define K 1000000007
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e3+5;
struct node{
	int val,id,discr;
}a[N];
int T,n,m;
ll tree[N][N],f[N][N],ans;
int lowbit(int x){return x&(-x);}
bool cmp(node x,node y){return x.val<y.val;}
bool cmp2(node x,node y){return x.id<y.id;}
void Discr()  //离散化
{
	sort(a+1,a+n+1,cmp);
	int tmp=0;
	a[1].discr=++tmp;
	for(int i=2;i<=n;i++)
	{
		if(a[i].val==a[i-1].val) a[i].discr=tmp;
		else a[i].discr=++tmp;
	}
	sort(a+1,a+n+1,cmp2);
}
void ins(int p,int locate,int x)
{
	for(;p<=n;p+=lowbit(p))
	{
		tree[p][locate]+=x;
		tree[p][locate]%=K;
		//if(tree[p][locate]>K) tree[p][locate]-=K; //会快很多
	}
}
ll query(int p,int locate)
{
	ll res=0;
	for(;p;p-=lowbit(p))
	{
		res+=tree[p][locate];
		res%=K;
		//if(res>K) res-=K;
	}
	return res;
}
int main(){
    scanf("%d",&T);
    int fad=T;
    while(T--)
    {
    	ans=0;
    	memset(tree,0,sizeof(tree));
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i].val);
    		a[i].id=i;
		}
		Discr();
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
			{
				if(j==1) f[i][j]=1;
				else
				{
					f[i][j]=query(a[i].discr-1,j-1);
					f[i][j]%=K;
					//if(f[i][j]>K) f[i][j]-=K;
				}
				ins(a[i].discr,j,f[i][j]);
			}
		for(int i=m;i<=n;i++)
		{
			ans+=f[i][m];  //统计
			ans%=K;
			//if(ans>K) ans-=K;
		}
		printf("Case #%d: %lld\n",fad-T,ans);
	}
    
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值