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

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(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

 

posted @ 2015-03-09 14:12  TCtower  阅读(225)  评论(0编辑  收藏  举报