题目: 点击打开链接
一共做了没几个题,感觉可以理解为这次是一个树状数组专题了。开始是不想用树状数组的,因为忘了大半了,在家里面手头又没有带笔记,只能找自己原来的代码和博客回顾了,也幸好代码都有注释,看自己写的还挺熟悉。
先总结下树状数组的大体内容:用logn的复杂度进行区间求和。然后涉及到具体内容包括单点更新区间查询、区间更新单点查询、区间更新区间查询,还有处理问题常见的就是逆序数了。前两个都比较简单看一眼就能想起来,区间更新区间查询的稍微难一点,一直也是糊里糊涂的状态,还是要理解过程记住代码。
关于逆序数,思路是重点,用结构体存点的下标id和值v,按照v排序,用b数组将v进行离散化存起来;再考虑到一个新数组c,代表一个长长的数轴,按照下标id的顺序将b的元素(也就是离散后的值v)存入,通过树状数组的方式操作c,求得(i-sum(b[i])),i是已存了多少个元素,sum(b[i])是在b[i]左边也就是比b[i]小的元素个数,做一个减法就是比b[i]大的元素个数,也就是逆序数,用sum求和即的结果。
这几个题都没有什么变形,直接可以当做模板的:
B:单点查询,区间求和
#include<iostream>
#include<cstring>
using namespace std;
const int MAX=100010;
int c[MAX];
int N, M;
inline int lowBit(int x)
{
return x & (-x);
}
void add(int pos, int v)
{
while(pos<=N+5)
{
c[pos]+=v;
pos+=lowBit(pos);
}
}
int getSum(int pos)
{
int sum=0;
while(pos)
{
sum+=c[pos];
pos-=lowBit(pos);
}
return sum;
}
void addInterval(int l, int r, int v)
{
add(l, v);
add(r+1, -v);
}
int queryInterval(int l, int r)
{
return getSum(r)-getSum(l-1);
}
int main()
{
int x, a, b;
char ch;
while(cin>>N>>M)
{
memset(c, 0, sizeof(c));
for(int i=1; i<=N; i++)
{
cin>>x;
add(i, x);
}
for(int i=0; i<M; i++)
{
cin>>ch;
if(ch=='C')
{
cin>>a>>b>>x;
addInterval(a, b, x);
}
else
{
cin>>a>>b;
cout<<queryInterval(a, b)<<endl;
}
}
}
return 0;
}
H:区间更新,区间求和
#include <iostream>
#include<cstring>
using namespace std;
const int MAX=100010;
int B[MAX], C[MAX];
inline int lowBit(int x)
{
return x & (-x);
}
void updata1(int *a, int pos, int v)//向上更新
{
while(pos && pos<MAX)
{
a[pos]+=v;
pos+=lowBit(pos);
}
}
int query1(int *a, int pos)//向下求和
{
int sum=0;
while(pos && pos<MAX)
{
sum+=a[pos];
pos-=lowBit(pos);
}
return sum;
}
void updata2(int *a, int pos, int v)//向下更新
{
while(pos && pos<MAX)
{
a[pos]+=v;
pos-=lowBit(pos);
}
}
int query2(int *a, int pos)//向上求和
{
int sum=0;
while(pos && pos<MAX)
{
sum+=a[pos];
pos+=lowBit(pos);
}
return sum;
}
inline void Insert(int pos, int v)//[1,p]加上d
{
updata1(B, pos, pos*v);//B【】向上更新 [p,MAX]加上 p*d
updata2(C, pos-1, v);//C【】向下更新 [1,p-1]加上 d
}
inline int Query(int pos)//求和
{
return query1(B, pos)+query2(C, pos)*pos;
}
inline void insertSeg(int a, int b, int v)//[a,b]加上d 区间修改
{
Insert(a-1, -v);//
Insert(b, v);
}
inline int querySeg(int a, int b)//[a,b]求和 区间查询
{
return Query(b)-Query(a-1);
}
int main()
{
int x, a, b;
int N, M;
char ch;
while(cin>>N>>M)
{
memset(C, 0, sizeof(C));
memset(B, 0, sizeof(B));
for(int i=2; i<=N+1; i++)
{
cin>>x;
insertSeg(i, i, x);
}
for(int i=0; i<M; i++)
{
cin>>ch;
if(ch=='C')
{
cin>>a>>b>>x;
a++; b++;
insertSeg(a, b, x);
}
else
{
cin>>a>>b;
a++; b++;
cout<<querySeg(a, b)<<endl;
}
}
}
return 0;
}
A:求逆序数(应用)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int MAXN=100010;
int c[MAXN];
int lowbit(int x)
{
return x & (-x);
}
int sum(int x)
{
int sum=0;
while(x>0)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
void add(int x, int v)
{
while(x<=MAXN)
{
c[x]+=v;
x+=lowbit(x);
}
}
struct node
{
int v;
int index;
bool operator <(const node& b)const
{
return v<b.v;
}
}nodes[MAXN];
int b[MAXN];//将初始数组重新赋值后 相对大小不变的新数组
int main()
{
int n;
while(cin>>n)
{
for(int i=1; i<=n; i++)
{
scanf("%d", &nodes[i].v);
nodes[i].index=i;
}
sort(nodes+1, nodes+n+1);//按大小排序
memset(b, 0, sizeof(b));
b[nodes[1].index]=1;
for(int i=2; i<=n; i++)//离散化
{
if(nodes[i].v==nodes[i-1].v)
b[nodes[i].index]=b[nodes[i-1].index];
else
b[nodes[i].index]=i;//这里的下标还是原来东西的下标
}
memset(c, 0, sizeof(c));
long long ans=0;
for(int i=1; i<=n; i++)
{
add(b[i], 1); //当前扫描的值是b[i],那么在x[b[i]]这个点上加1,表示又出现了1个b[i]值
ans+=(i-sum(b[i]));// i当前插入几个数 sum(b[i])比b[i]小的数的个数 相减得逆序数
//ans += sum(n)-sum(b[i]);
//cout<<i-sum(b[i])<<" "<<i<<endl;
}
cout<<ans<<endl;
}
}