BZOJ 3289 Mato的文件管理 莫队算法+树状数组

题目大意:给出一段序列,求一个区间内的逆序对数量.


思路:又是没有修改的查询操作,又可以搞莫队了(莫队真好搞..

先把所有的询问排序,然后从头到位进行转移,记一个全局的答案,然后每次转移的时候记录逆序对的改变情况.然后从ans数组中输出..


CODE:


#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 50010
using namespace std;

struct Ask{
	int x,y,_id;
	int block;
	
	bool operator <(const Ask &a)const {
		if(block == a.block)	return y < a.y;
		return block < a.block;
	}
	void Read(int p) {
		scanf("%d%d",&x,&y);
		_id = p;
	}
}ask[MAX];

int cnt,asks;
int src[MAX];
int fenwick[MAX];
pair<int,int *> xx[MAX];

int ans[MAX];

inline int GetSum(int x)
{
	if(!x)	return 0;
	int re = 0;
	for(; x; x -= x&-x)
		re += fenwick[x];
	return re;
}

inline void Fix(int x,int c)
{
	for(; x <= cnt; x += x&-x)
		fenwick[x] += c;
}

int main()
{
	cin >> cnt;
	for(int i = 1; i <= cnt; ++i)
		scanf("%d",&xx[i].first),xx[i].second = &src[i];
	sort(xx + 1,xx + cnt + 1);
	int t = 0;
	for(int i = 1; i <= cnt; ++i) {
		if(!t || xx[i].first != xx[i - 1].first)
			++t;
		*xx[i].second = t;
	}
	cin >> asks;
	for(int i = 1; i <= asks; ++i) {
		ask[i].Read(i);
	}
	int block = static_cast<int>(sqrt(cnt));
	for(int i = 1; i <= asks; ++i)
		ask[i].block = ask[i].x / block;
	sort(ask + 1,ask + asks + 1);
	int l = 1,r = 0;
	int now = 0;
	for(int i = 1; i <= asks; ++i) {
		while(r < ask[i].y) {
			int total = r - l + 1,larger = total - GetSum(src[++r]);
			now += larger;
			Fix(src[r],1);
		}
		while(l > ask[i].x) {
			now += GetSum(src[--l]);
			Fix(src[l],1);
		}
		while(r > ask[i].y) {
			int total = r - l + 1,larger = total - GetSum(src[r]);
			now -= larger;
			Fix(src[r--],-1);
		}
		while(l < ask[i].x) {
			now -= GetSum(src[l] - 1);
			Fix(src[l++],-1);
		}
		ans[ask[i]._id] = now;
	}
	for(int i = 1; i <= asks; ++i) 
		printf("%d\n",ans[i]);
	return 0;
}


你可能感兴趣的:(树状数组,逆序对,莫队算法,bzoj)