1.楼兰图腾
一遍从左边向右边统计,所以当遍历到yi的时候,前面1-i-1个y的高度都已经被加入了树状数组
所以yi左边比它高度低的就是sum(yi-1),左边比它高度高的就是sum(n)-sum(y1)
再一遍从右边向左边统计,所以当遍历到yi的时候,前面的所有高度都已经被加入了树状数组
所以yi右边比它高度低的就是sum(yi-1),右边比它高的就是sum(n)-sum(y1)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010;
int a[N];
int n;
int tr[N];//树状数组tr[i]表示高度y=i出现了几次
int lower[N],higher[N];
typedef long long LL;
int lowbit(int x)
{
return x & -x;
}
void add(int x,int c)
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}
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];
for(int i=1;i<=n;i++)
{
int y=a[i];
higher[i]=sum(n)-sum(y);
lower[i]=sum(y-1);
add(y,1);
}
memset(tr,0,sizeof tr);
LL res1=0,res2=0;
for(int i=n;i>=1;i--)
{
2.一个简单的整数问题
树状数组两个操作:
1.给a[x]+c
2.求区间和
本题要求操作:
1.给区间每个数+c------转化成差分数组b[l]+=c,b[r+1]-=c
2.求单点值------求差分数组区间和
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
const int N = 1e5+10;
int a[N];
int tr[N];
typedef long long LL;
int lowbit(int x) // 返回末尾的1
{
return x & -x;
}
void add(int x,int c)
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}
LL sum(int x)
{
LL res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) add(i,a[i]-a[i-1]);
while (m -- )
{
char op;
cin>>op;
if(op=='C')
{
int l,r,d;
cin>>l>>r>>d;
add(l,d);
add(r+1,-d);
}
else
{
int x;
cin>>x;
cout<<sum(x)<<endl;
}
}
return 0;
}
3.一个简单的整数问题2
解法:
首先,给区间每个数+c需要tr维护差分数组
add(l,c) add(r+1,-c)即可完成第一个目标
然后要求原数组的区间和,相当于每个差分数组求区间和,得到原数组,再将原数组求和,得到原数组的区间和。
如图所示:d是差分数组,a是原数组
要求的就是那个三角形的和,把它补全成一个矩形
所以三角形的和就==(n+1)*(d1+d2+...+dn) - (1*d1+2*d2+....+ndn)
所以需要一个树状数组tr1[]来维护 d[i]的前缀和,一个树状数组tr2[]来维护id[i]前缀和
要求的前缀和就是(x+1)*sum(tr1,x)-sum(tr2,x)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m;
const int N = 1e5+10;
int a[N];
typedef long long LL;
LL tr1[N],tr2[N];
int lowbit(int x) // 返回末尾的1
{
return x & -x;
}
void add(LL tr[],int x,LL c)
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}
LL sum(LL tr[],int x)
{
LL res=0;
for(int i=x;i;i-=lowbit(i)) res+=tr[i];
return res;
}
LL premix_sum(int x)
{
return (x+1)*sum(tr1,x)-sum(tr2,x);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
int b=a[i]-a[i-1];
add(tr1,i,b);
add(tr2,i,(LL)i*b);
}
while (m -- )
{
char op;
cin>>op;
if(op=='C')
{
int l,r,d;
cin>>l>>r>>d;
add(tr1,l,d),add(tr1,r+1,-d);
add(tr2,l,l*d),add(tr2,r+1,-(r+1)*d);
}
else
{
int l,r;
cin>>l>>r;
cout<<premix_sum(r)-premix_sum(l-1)<<endl;
}
}
return 0;
}
4.谜一样的牛
初始有1-n个位置,等着n头牛来占
从最后一头牛开始,它比前面k个牛高,那么它的高度就是k+1,在1-n个位置中将第k+1的位置删掉
(因为它被第n头牛占住了)
倒数第二头牛,它比前面k个牛高,那我它的高度就是还剩下位置中左向右数第k+1个位置,
再删掉该位置(因为它被倒数第二个牛占住了)
所以每次的操作就是取已有位置中第k+1个位置为高度,再删掉这个数
1.找到剩余数中第k小的数,---二分
2.删掉一个数---tr树状数组
由于树状数组中没删掉的数是1,删掉的数是0,所以求前缀和,第一次等于k的x就是第k小的数
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
const int N = 1e5+10;
int h[N];
int tr[N];
int lowbit(int x)
{
return x&-x;
}
void add(int x,int c)
{
for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}
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=2;i<=n;i++) cin>>h[i];
for(int i=1;i<=n;i++) add(i,1);
int ans[n];//存最后结果
for(int i=n;i;i--)
{
int k=h[i]+1;//当前未被牛占的位置,前面有h[i]头牛矮于当前牛,当前牛放在第h[i]+1的位置
int l=1,r=n;
while(l<r)
{
int mid=(l+r)/2;
if(sum(mid)>=k) r=mid;
else l=mid+1;
}
ans[i]=l;
add(l,-1);
}
for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
return 0;
}