【UVA, 11990】“Dynamic” Inversion【平方分割n】

看完之后可以关注我的新浪微博哦:吐舌头    @晨阳微光(虽然和ACM毫无关系)

题目类型:数据结构   线段树/平方分割 分桶法

复杂度:On*sqrt(n)

题目含义:

给出序列一个序列由n个数组成(这n个数互不相同)。

m次询问,每次询问有一个值,进行的操作是减去序列中等于这个值的数,并输出在去掉这个值之前的这个序列的逆序数对

样例:

5 4                             (1,5,3,4,2)  ->  (1,3,4,2)  ->  (3,4,2)  ->  (3,2)  ->  (2)

4

2

问题分析及解法:关于序列S,如果将下标为i的数映射到二维坐标系上面的话,他的坐标为(i,S[i]),那么在坐标系上这个点左上和右下的点的个数之和就是这个数的逆序对个数。

解法就算是分桶法。

转载分桶法的理解:传送门

通过分桶法将平面分割为sqrt(n)*sqrt(n)。

每个桶维护两个信息:一是这个桶里的点的个数;二是同一行桶的个数的前缀和,用前缀和来做区域内的查询。

下面来看代码:

#include 
   
   
    
    
#include 
    
    
     
     
#define MAX_N 200016
#define MAX_M 100016
#define BUCKET_SIZE 450
using namespace std;
//
int n, m;
//
struct BUCKET
{
	int  cnt;//桶内有几个点
	int  prefix_sum;//这个桶之前(算这个)有多少个点,在这一行
}bucket[BUCKET_SIZE][BUCKET_SIZE];
struct SPACE
{
	int X[MAX_N], Y[MAX_N];
	void init() {
		memset(X, -1, sizeof(X));
		memset(Y, -1, sizeof(Y));
	}
	void insert(int x,int y) {
		X[y] = x;
		Y[x] = y;
	}
	void remove(int x, int y) {
		X[y] = -1;
		Y[x] = -1;
	}
	int getx(int y) {
		return X[y];
	}
	int gety(int x) {
		return Y[x];
	}
}space;

//更新桶的前缀和
void updata_prefix_sum(int x, int y) {
	int sum = (x > 0 ? bucket[x - 1][y].prefix_sum : 0);
	for (int i = x; i < BUCKET_SIZE; i++) {
		sum += bucket[i][y].cnt;
		bucket[i][y].prefix_sum = sum;
	}
}

//在平面区域加一个点
void add(int x,int y) {
	space.insert(x, y);
	int bucket_x = x / BUCKET_SIZE;
	int bucket_y = y / BUCKET_SIZE;
	bucket[bucket_x][bucket_y].cnt++;
	updata_prefix_sum(bucket_x, bucket_y);
}

//在平面区域删除一个点
void remove(int x,int y) {
	space.remove(x, y);
	int bucket_x = x / BUCKET_SIZE;
	int bucket_y = y / BUCKET_SIZE;
	bucket[bucket_x][bucket_y].cnt--;
	updata_prefix_sum(bucket_x, bucket_y);
}
//(0,0)到(x,y)有多少个点
int count_sum(int x,int y) {
	int sum = 0;
	int plane_x = (x + 1) / BUCKET_SIZE;
	int plane_y = (y + 1) / BUCKET_SIZE;
	for (int i = 0; i < plane_y; i++) {
		if (plane_x > 0)sum += bucket[plane_x - 1][i].prefix_sum;
	}
	for (int i = plane_x*BUCKET_SIZE; i <= x; i++) {
		if (space.gety(i) != -1 && space.gety(i) < plane_y*BUCKET_SIZE) {
			sum++;
		}
	}
	for (int i = plane_y*BUCKET_SIZE; i <= y; i++) {
		if (space.getx(i) != -1 && space.getx(i) <= x) {
			sum++;
		}
	}
	return sum;
}
int count_r_sum(int x, int y) {
	int r = 0;
	int tmp = count_sum(x, y);
	r += count_sum(n - 1, y) - tmp;
	r += count_sum(x, n - 1) - tmp;
	return r;
}
int main() {

	while (cin >> n >> m) {
		space.init();
		memset(bucket, 0, sizeof(bucket));
		long long result = 0;
		for (int i = 0; i < n; i++) {
			int num;
			cin >> num;
			add(i, num - 1);
			result += count_r_sum(i, num - 1);
			//cout<
     
     
      
      <
      
      
       
       > num;
			num--;
			cout << result << endl;
			result -= count_r_sum(space.getx(num), num);
			remove(space.getx(num), num);
		}
	}
	return  0;
}
      
      
     
     
    
    
   
   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值