0x42 树状数组

目录

模板

poj2299

题目链接

题目

题解

代码

CH4201 多种逆序对求法

题目链接

题目

题解

代码

poj3468

题目链接

题目

题解

代码


模板

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 多种逆序对求法

题目链接

  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个操作;

  1. 第一类指令形如"C l r d", 表示把数列中第 l~r 个数都加上d;
  2. 第二类指令形如”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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总想玩世不恭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值