对于序列A,它的逆序对数定义为满足
i<
j,且A
i>A
j的数对(
i,
j)的个数。给1到
n的一个排列,按照某种顺序依次删除
m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
N<=100000 M<=50000
【分析】
这种水题还弄了两节课...真是没法治了。
用很多种方法,最好理解的就是块状链表套树状数组,每个块状链表里面套一个二维的树状数组,再加上离散化。
将m序列中的每一个数字对应一个坐标(在n中坐标,n-数字大小)然后就可以做了。
我还开了3个一维树状数组,卡着时间过的。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <vector> 6 #include <utility> 7 #include <iomanip> 8 #include <string> 9 #include <cmath> 10 #include <queue> 11 #include <assert.h> 12 #include <map> 13 14 const int N = 100000 + 10; 15 const int SIZE = 225;//块状链表的根号50000 16 const int M = 50000 + 5; 17 using namespace std; 18 typedef long long ll; 19 int lowbit(int x) {return x & -x;} 20 struct BLOCK_LIST{ 21 int C[SIZE][SIZE]; 22 int t[2][SIZE];//关于a的离散化序列和b的离散化序列 23 void init(){ 24 memset(C, 0, sizeof(C)); 25 memset(t, 0, sizeof(t)); 26 } 27 int sum(int x, int y){ 28 int cnt = 0, f = y; 29 //int flag = x; 30 while (x > 0){ 31 while (y > 0){ 32 cnt += C[x][y]; 33 y -= lowbit(y); 34 } 35 x -= lowbit(x); 36 y = f; 37 } 38 return cnt; 39 } 40 void add(int x, int y){ 41 int f = y; 42 while (x < SIZE){ 43 while (y < SIZE){ 44 C[x][y]++; 45 y += lowbit(y); 46 } 47 x += lowbit(x); 48 y = f; 49 } 50 return; 51 } 52 //二分搜索,查找x在k内相当的值 53 int search(int k, int x){ 54 int l = 0, r = 224, Ans; 55 while (l <= r){ 56 int mid = (l + r) >> 1; 57 if (t[k][mid] <= x) Ans = mid, l = mid + 1; 58 else r = mid - 1; 59 } 60 return Ans; 61 } 62 }list[SIZE]; 63 struct DATA{ 64 int t[2];//影响m的树状数组的两个值,注意都要进行离散化 65 int x;//值 66 }rem[M]; 67 struct LSH{ 68 int num, order; 69 bool operator < (LSH b)const{ 70 return num < b.num;//专门用来离散化 71 } 72 }A[M]; 73 int data[N], c[3][N], n, m; 74 int num[N];//num[N]代表i有i存在的逆序对的个数 75 ll tot;//记录逆序对的个数 76 77 ll sum(int k, int x){ 78 ll cnt = 0; 79 while (x > 0){ 80 cnt += c[k][x]; 81 x -= lowbit(x); 82 } 83 return cnt;//记得要用ll 84 } 85 void add(int k, int x){ 86 while (x <= n){ 87 c[k][x]++; 88 x += lowbit(x); 89 } 90 return; 91 } 92 void init(){ 93 tot = 0; 94 memset(num, 0, sizeof(num)); 95 memset(c, 0, sizeof(c)); 96 scanf("%d%d", &n, &m); 97 for (int i = 1; i <= n; i++){ 98 int x; 99 scanf("%d", &x); 100 data[x] = i; 101 int tmp = sum(0, x); 102 num[x] += (i - 1 - tmp);//先求出在i之前的比i大的数 103 num[x] += (x - tmp - 1);//后面比i小的数 104 tot += (i - 1 - tmp); 105 add(0, x); 106 } 107 //printf("%d\n", tot); 108 //for (int i = 1; i <= n; i++) printf("%d\n", num[i]); 109 } 110 //离散化 111 void prepare(){ 112 //a,b中两个值分别为位置和大小 113 for (int i = 1; i <= m; i++){ 114 int tmp; 115 scanf("%d", &tmp); 116 rem[i].t[0] = data[tmp]; 117 rem[i].t[1] = n - tmp + 1; 118 rem[i].x = tmp; 119 } 120 //for (int i = 1; i <= m; i++) printf("%d %d %d\n", rem[i].t[0], rem[i].t[1], rem[i].x); 121 } 122 void get(int k, int l, int r, int x){ 123 int cnt = r - l + 1, pos = 1; 124 for (int i = l; i <= r; i++){ 125 A[pos].order = i; 126 A[pos].num = rem[i].t[k]; 127 pos++; 128 } 129 sort(A + 1, A + cnt + 1); 130 for (int i = 1;i <= cnt; i++) list[x].t[k][i] = A[i].num; 131 for (int i = 1;i <= cnt; i++) rem[A[i].order].t[k] = i; 132 } 133 void work(){ 134 for (int i = 0; i < SIZE; i++) list[i].init(); 135 int cnt = 0;//cnt是用来记录块的数量 136 for (int pos = 1; pos <= m; pos++){ 137 138 int l = pos; 139 l = min(m , 224 + pos - 1); 140 //从[l,m]这一段放在list[cnt]里面 141 get(0, pos, l, cnt); 142 get(1, pos, l, cnt); 143 for (int i = pos; i <= l; i++){ 144 printf("%lld\n", tot); 145 int tmp = list[cnt].sum(rem[i].t[0], rem[i].t[1]); 146 for (int j = 0; j < cnt; j++) tmp += list[j].sum(list[j].search(0, list[cnt].t[0][rem[i].t[0]]), list[j].search(1, list[cnt].t[1][rem[i].t[1]])); 147 148 tot -= (num[rem[i].x] - (tmp + (sum(2, rem[i].x) - (sum(1, list[cnt].t[0][rem[i].t[0]]) - tmp)))); 149 list[cnt].add(rem[i].t[0], rem[i].t[1]); 150 add(1, list[cnt].t[0][rem[i].t[0]]); 151 add(2, rem[i].x); 152 } 153 cnt++; 154 pos = l; 155 } 156 } 157 158 int main(){ 159 #ifdef LOCAL 160 freopen("data.txt", "r", stdin); 161 freopen("out.txt", "w", stdout); 162 #endif 163 init(); 164 prepare(); 165 if (m == 0) return 0; 166 else work(); 167 return 0; 168 }