目录
模板
inline int lowbit(int x) {return x&-x;}
void add(int x,int y) {
while(x<=n) {
c[x] += y;
x += lowbit(x);
}
}
int ask(int x) {
ll res = 0;
while(x>0) {
res += c[x];
x -= lowbit(x);
}
return res;
}
poj2299
题目链接
http://poj.org/problem?id=2299
题目
求有多少逆序对。 举例:1,2,3有3对逆序对,而3,2,1没有逆序对
题解
由于排序规则定义是由大到小,所以在将a[i]加入树状数组前,先求1-a[i-1]的和,这就是与第i个元素构成的逆序对的个数。
由于数据规模很大,需要先离散化一下。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 1000005;
const int N = 105;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
ll n,a[maxn],c[maxn*4],ans,f[maxn],temp[maxn];
inline ll lowbit(int x) {return x&(-x);}
void add(ll x,ll y) {
while(x<=n) {
c[x] += y;
x += lowbit(x);
}
}
ll ask(ll x) {
ll res = 0;
while(x>0) {
res += c[x];
x -= lowbit(x);
}
return res;
}
void discrete() {
for(int i=0;i<n;i++) temp[i] = a[i];
sort(temp,temp+n);
f[0] = temp[0];
int cnt = 1;
for(int i=1;i<n;i++) {
if(temp[i]!=temp[i-1]) { //去重
f[cnt++] = temp[i];
}
}
n = cnt;
}
int main()
{
while(cin>>n&&n) {
INIT(c);
ans = 0;
for(int i=0;i<n;i++) {read(a[i]);}
discrete();
for(int i=n-1;i>=0;i--) {
int x = lower_bound(f,f+n,a[i])-f+1;
ans += ask(x-1);
add(x,1);
}
cout<<ans<<endl;
}
return 0;
}
CH4201 多种逆序对求法
题目链接
题目
如果三个数满足x1<x2<x3并且y1>y2<y3, 那么称为“v”字型;
如果三个数满足x1<x2<x3并且y1<y2>y3, 那么称为“^”字型;
给定n个数,求“v”字型和“^"字型各有多少对。
题解
以v字形为例,如果遍历到了a[i],那么记录i前面比它大的数有left[i]个,它后面比它大的数有right[i]个,那么这一个数能形成的v字形的对数就是 left[i]*right[i] 个,其他同理。
这里面涉及到很多种逆序对的求法。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
const int N = 105;
inline void read(int &x) {
int f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,a[maxn];
ll ans1[maxn],ans2[maxn],c[maxn],ans,tot;
inline int lowbit(int x) {return x&(-x);}
void add(int x,int y) {
while(x<=n) {
c[x] += y;
x += lowbit(x);
}
}
ll query(ll x) {
ll res = 0;
while(x>0) {
res += c[x];
x -= lowbit(x);
}
return res;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++) {
read(a[i]);
ans1[i] = query(a[i]-1);
add(a[i],1);
}
INIT(c);
for(int i=n-1;i>=0;i--) { //倒序来求一个数右边比它小的数的个数
ans2[i] = query(a[i]-1);
add(a[i],1);
}
for(int i=0;i<n;i++) ans += ans1[i]*ans2[i];
for(int i=0;i<n;i++) {
ans1[i] = i-ans1[i]; //一个数左边比它大的数的个数可以算出来
ans2[i] = n-i-1-ans2[i];
tot += ans1[i]*ans2[i];
}
cout<<tot<<" "<<ans<<endl;
return 0;
}
poj3468
题目链接
http://poj.org/problem?id=3468
题目
首先输入n个数,然后有q个操作;
- 第一类指令形如"C l r d", 表示把数列中第 l~r 个数都加上d;
- 第二类指令形如”Q l r", 表示询问数列中第 l~r个 数的和。
题解
对于区间修改来说,l~r都加上d,那么可以 b[l] += d, b[r+1] -= d,用差分的形式来维护;
这样如果直接对某一个点i求和,那么求出来的并不是a[1]~a[i]的前缀和,而就是a[i]的值;
在对区间求和的时候,可以参考下式:
变成这种形式,第一个前缀和就用正常的树状数组来维护,第二个另开一个树状数组来维护 i*b[i] 的前缀和。
考虑到刚开始有初始值,这一部分可以先对a数组预处理,它们初始值的和就是sum[r]-sum[l-1]。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;
#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
const int N = 105;
inline void read(ll &x) {
ll f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
x*=f;
}
ll n,q,a[maxn],c[2][maxn],sum[maxn];
string s;
inline int lowbit(int x) {return x&(-x);}
void add(int k,int x,int d) {
while(x<=n) {
c[k][x] += d;
x += lowbit(x);
}
}
ll query(int k,int x) {
ll res = 0;
while(x>0) {
res += c[k][x];
x -= lowbit(x);
}
return res;
}
int main()
{
read(n),read(q);
for(int i=1;i<=n;i++) {
read(a[i]);
sum[i] = sum[i-1]+a[i];
}
while(q--) {
cin>>s;
if(s=="C") {
ll l,r,d;
read(l),read(r),read(d);
add(0,l,d);
add(0,r+1,-d);
add(1,l,l*d);
add(1,r+1,-(r+1)*d);
}
else {
ll l,r;
read(l),read(r);
ll ans = sum[r]+query(0,r)*(r+1)-query(1,r);
ans -= sum[l-1]+query(0,l-1)*l-query(1,l-1);
cout<<ans<<endl;
}
}
return 0;
}