【BZOJ3295】【块状链表+树状数组】动态逆序对

Description

对于序列A,它的逆序对数定义为满足 i< j,且A i>A j的数对( i, j)的个数。给1到 n的一个排列,按照某种顺序依次删除 m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数 nm,即初始元素的个数和删除的元素个数。以下 n行每行包含一个1到 n之间的正整数,即初始排列。以下 m行每行一个正整数,依次为每次删除的元素。
 

Output

 
输出包含 m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

 

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 }
View Code

 

你可能感兴趣的:(树状数组)