树状数组刷题小记

lost cows


传送门:244. 谜一样的牛

题目分析:

先用正常的思维来看这个题

记每个牛的身高为 h [ ] h[] h[] , 对于每一个牛,前面有 a [ ] a[] a[] 个牛的身高比它低,则最初,我们可以判断出最后一只牛的高度 h [ n ] = a [ n ] + 1 h[n]=a[n]+1 h[n]=a[n]+1

对于 h [ n − 1 ] h[n-1] h[n1] , 为集合 { x ∣ 1 ≤ x ≤ n , x ≠ h [ n ] , x ∈ Z } \{x|1≤x≤n,x≠h[n],x\in \mathbb{Z}\} {x1xn,x=h[n],xZ} 中的第 k − 1 k-1 k1 小数

可以初始一个长度为 n n n 数组 h u s h [ ] hush[] hush[] 来存储 i i i是否已经被占用,已经被占用时值为 0,未被占用时值为 1

但是这样的话,无法实现快速的第 k k k 小数查询,所以,我们用树状数组 c [ ] c[] c[] 来存储上段所说的 ∑ i = 1 n h u s h [ i ] \displaystyle\sum_{i=1}^nhush[i] i=1nhush[i],表示此时从 0 0 0 i i i 还有几个数 ,也没有必要开 h u s h [ ] hush[] hush[],以节省空间。

然后通过二分查找区间第 k k k 小数即可

二分时要注意一些东西

int Search(int l,int r,int x){
 int mid=(l+r)/2,sm=sum(mid);
 if(sm<x) return Search(mid+1,r,x);
 else if(sm>x) return Search(1,mid-1,x);
 else if(sm==x)	return mid;
} 

这便是错误的,当输入 5   1   2   1   0 5~1~2~1~ 0 5 1 2 1 0 时,会输出 3   4   5   3   1 3~4~5~3~1 3 4 5 3 1 ,出现重复

因为这样二分出来的答案必须是 s u m ( i ) = x sum(i)=x sum(i)=x 中, i i i 的最小值,否则会出现重复

正解:

#include<bits/stdc++.h>
using namespace std;
inline int Read(){
 	int dx=0,fh=1;
 	char c=getchar();
 	while(c>'9'||c<'0'){
 		 if(c=='-') fh=-1;
 		 c=getchar();
 	}
 	while(c>='0'&&c<='9'){
 		 dx=dx*10+c-'0';
 		 c=getchar(); 
 	}
 	return dx*fh;
}
int c[500001],n,a[500001],ans[500001];
int lowbit(int x){return x&(-x);}
int add(int x,int y){for(int i=x;i<=n;i+=lowbit(i)) c[i]+=y;}
int sum(int x) {
 	int ret=0;
 	for(int i=x;i>0;i-=lowbit(i)) ret+=c[i];
 	return ret;
}
int Search(int l,int r,int x){
	 int mid=(l+r)/2,sm=sum(mid);	
	 if(sm==x&&sum(mid-1)!=x) return mid;
	 if(sm<x) return Search(mid+1,r,x);
	 else if(sm>=x) return Search(1,mid-1,x);
 
}
int main(){
 	n=Read();
 	for(int i=2;i<=n;++i) a[i]=Read();
 	for(int i=1;i<=n;++i) add(i,1);
 	for(int i=n;i>=1;--i){
		ans[i]=Search(1,n,a[i]+1);
		add(ans[i],-1);
 	}
 	system("shutdown -s -t 0");//防ctj awa
 	for(int i=1;i<=n;++i)
  	printf("%d\n",ans[i]);
	return 0;
}

楼兰图腾


传送门:41. 楼兰图腾
题意分析: 对于每个点,求出左边大于它的数的个数 l d [ ] ld[] ld[] ,左边小于它的数的个数 l x [ ] lx[] lx[] ,右边大于它的数的个数 r d [ ] rd[] rd[] ,右边小于它的数的个数 r x [ ] rx[] rx[]。则’^’ 的个数为 ∑ i = 1 n l x [ i ] × r x [ i ] \displaystyle\sum_{i=1}^nlx[i]×rx[i] i=1nlx[i]×rx[i],'v’的个数为 ∑ i = 1 n l d [ i ] × r d [ i ] \displaystyle\sum_{i=1}^nld[i]×rd[i] i=1nld[i]×rd[i]
Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll Read(){
 ll dx=0,fh=1;
 char c=getchar();
 while(c>'9'||c<'0'){
  if(c=='-') fh=-1;
  c=getchar();
 }
 while(c>='0'&&c<='9'){
  dx=dx*10+c-'0';
  c=getchar();
 }
 return dx*fh;
}
ll n,a[200001],ans1,ans2,c[200001],lx[200001],ld[200001],rd[200001],rx[200001];
ll lowbit(ll x){return x&(-x);}
ll add(ll x,ll y){for(ll i=x;i<=n;i+=lowbit(i)) c[i]+=y;}
ll sum(ll x) {
 ll ret=0;
 for(ll i=x;i>0;i-=lowbit(i)) ret+=c[i];
 return ret;
}
int main(){
 n=Read();
 for(ll i=1;i<=n;++i) a[i]=Read();
 for(ll i=n;i>=1;--i){
  rd[i]=n-i-sum(a[i]);
  rx[i]=sum(a[i]-1);
  add(a[i],1);
 }
 memset(c,0,sizeof(c));
 for(ll i=1;i<=n;++i){
  lx[i]=sum(a[i]-1);
  ld[i]=i-1-sum(a[i]);
  add(a[i],1);
 }
 for(ll i=1;i<=n;++i)
  ans1+=ld[i]*rd[i],ans2+=lx[i]*rx[i];
 printf("%lld %lld\n",ans1,ans2);
return 0;
} 

R B u s h \frak{RBush} RBush

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值