逆序对:从一个长N的序列中逐渐移除M个数,求每次移除前序列的逆序对的个数?
3.3活用各种数据结构
线段树和平方分割
如果将第i个数映射到点(i,X_i)的话,那么这个点左上和右下的点的个数之和就是逆序对个数。通过分桶法将平面分割为sqrt(N)*sqrt(N),每个桶维护两个信息,一是这个桶里的点的个数,二是同一行桶的个数的前缀和,有了前缀和做区域内的查询非常方便。
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define MAX_N 200000 + 16 #define MAX_M 100000 + 16 #define BUCKET_SIZE 450 // sqrt(MAX_N) = 447 int A[MAX_N], N, M; struct Bucket { int count; // 内部数字的个数 int prefix_sum; // 前缀和 }bucket[BUCKET_SIZE][BUCKET_SIZE]; // 平面坐标快速查询 struct Space { int X[MAX_N], Y[MAX_N]; void insert(const int& x, const int& y) { X[y] = x; Y[x] = y; } void remove(const int& x, const int& y) { X[y] = -1; Y[x] = -1; } int getX(const int& y) { return X[y]; } int getY(const int& x) { return Y[x]; } void init() { memset(X, -1, sizeof(X)); memset(Y, -1, sizeof(Y)); } } space; void update_prefix_sum(int bx, int by) { int sum = (bx > 0 ? bucket[bx - 1][by].prefix_sum : 0); for (int i = bx; i < BUCKET_SIZE; ++i) { sum += bucket[i][by].count; bucket[i][by].prefix_sum = sum; } } // 加入一个点 void add(int x, int y) { space.insert(x, y); int bx = x / BUCKET_SIZE; int by = y / BUCKET_SIZE; ++bucket[bx][by].count; update_prefix_sum(bx, by); } // 删除一个点 void remove(int x, int y) { space.remove(x, y); int bx = x / BUCKET_SIZE; int by = y / BUCKET_SIZE; --bucket[bx][by].count; update_prefix_sum(bx, by); } // (0,0)与(x,y)围起来的矩形区域的点的个数 int count_sum(int x, int y) { int block_w = (x + 1) / BUCKET_SIZE; int block_h = (y + 1) / BUCKET_SIZE; int count = 0; // 完全在内部的桶 for (int i = 0; i < block_h; ++i) { if (block_w > 0) { count += bucket[block_w - 1][i].prefix_sum; } } // 其他 for (int i = block_w * BUCKET_SIZE; i <= x; ++i) { if (space.getY(i) != -1 && space.getY(i) < block_h * BUCKET_SIZE) count++; } for (int i = block_h * BUCKET_SIZE; i <= y; ++i) { if (space.getX(i) != -1 && space.getX(i) <= x) count++; } return count; } // (x,y)的左上和右下方块内部点的个数就是逆序数对的个数 int count_inversion(int x, int y) { int count = 0; int intersection = count_sum(x, y); count += count_sum(x, N - 1) - intersection; // 左上 count += count_sum(N - 1, y) - intersection; // 右下 return count; } ///////////////////////////SubMain////////////////////////////////// int main(int argc, char *argv[]) { #ifndef ONLINE_JUDGE freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif while (scanf("%d %d", &N, &M) != EOF) { space.init(); memset(bucket, 0, sizeof(bucket)); for (int i = 0; i < N; ++i) { scanf("%d", &A[i]); --A[i]; } long long inversion = 0; for (int i = 0; i < N; ++i) { add(i, A[i]); inversion += count_inversion(i, A[i]); } for (int i = 0; i < M; ++i) { int q; scanf("%d", &q); --q; printf("%lld\n", inversion); inversion -= count_inversion(space.getX(q), q); remove(space.getX(q), q); } } #ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); system("out.txt"); #endif return 0; } ///////////////////////////End Sub//////////////////////////////////
13982065 | 11990 | “Dynamic'' Inversion | Accepted | C++ | 2.062 | 2014-08-02 19:05:01 |
这个方法是从http://quiz.fuqinho.net/blog/2012/06/14/uva-11990-dynamic-inversion/学过来的,原作者注释有些小问题。
知识共享署名-非商业性使用-相同方式共享:码农场 » UVa 11990 Inversion 题解 《挑战程序设计竞赛》