复健计划(4)并查集
因为前段时间刚刚复习了几道并查集题目,还算比较熟悉,就先放在这里:
板子题(程序自动分析,NOI2015)
带边权的并查集1(POJ 1733)
带边权的并查集2(银河英雄传说,NOI2002)
带扩展域的并查集(POJ 1733)
复健计划(5)树状数组
树状数组是一个好用,好写但是代码比较抽象的数据结构,总之记住 c [ x ] c[x] c[x] 表示 a a a 数组 [ x − l o w b i t ( x ) + 1 , x ] [x-lowbit(x)+1,x] [x−lowbit(x)+1,x]的和,其中 l o w b i t ( x ) lowbit(x) lowbit(x) 表示二进制表示下从右往左第一个1和末位的0所构成的数值.
1、最基础的题 acwing 242
题目大意:区间修改,单点查询
思路:用树状数组维护原序列的差分序列,注意修改时要保证r+1~n部分的数字不变.
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=100010;
int a[N],n,m;
int b[N];
int c[N];
int lowbit(int x){
return x&-x;
}
void add(int x,int y){
for(;x<=n;x+=lowbit(x)) c[x]+=y;
}
int sum(int x){
int ans=0;
for(;x;x-=lowbit(x)) ans+=c[x];
return ans;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i]-a[i-1];
for(int i=1;i<=n;i++) add(i,b[i]);
for(int i=1;i<=m;i++){
int l,r,d,x;
char op[2];
scanf("%s",op);
if(op[0]=='Q'){
scanf("%d",&x);
printf("%d\n",sum(x));
}
else{
scanf("%d%d%d",&l,&r,&d);
add(l,d);
add(r+1,-d);
}
}
return 0;
}
2、需要提取模型的题目 acwing 241
题目大意:给一个1~n的排列,求三元组
(
a
,
b
,
c
)
(a,b,c)
(a,b,c)满足条件
a
<
b
,
b
>
c
a<b,b>c
a<b,b>c的个数;
思路:针对该排列中任意一个位置
i
i
i ,其高度为
y
i
y_i
yi ,则要求找到
1
i
−
1
1~i-1
1 i−1 位置中比
y
i
y_i
yi 大的数的个数和
i
+
1
n
i+1~n
i+1 n 位置中比
y
i
y_i
yi 大的个数,相乘即可;具体实现时,求前者时循环从1到n,这样不会算入i后满足条件的高度数量,求后者时同理;并且此题以高度为原数组,这就是转换,也是难点.
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200010;
typedef long long ll;
int a[N],n;
int c[N];
ll gr[N],le[N];
ll ans1,ans2;
int lowbit(int x){
return x&-x;
}
void add(int x,int y){
for(;x<=n;x+=lowbit(x)) c[x]+=y;
}
ll sum(int x){
ll ans=0;
for(;x;x-=lowbit(x)) ans+=(ll)c[x];
return ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
add(a[i],1);
gr[i]=sum(n)-sum(a[i]);
le[i]=sum(a[i]-1);
}
memset(c,0,sizeof(c));
for(int i=n;i;i--){
add(a[i],1);
ans1+=gr[i]*(sum(n)-sum(a[i]));
ans2+=le[i]*sum(a[i]-1);
}
cout<<ans1<<" "<<ans2<<endl;
return 0;
}