BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)

Orz黄学长,蒟蒻在黄学长的带领下,通过阅读黄学长的代码!终于会了这道题! 首先我想先说一下这道题的思路(准确来说是黄学长的)。 很明显,树状数组应该不用讲吧!关键是内存怎么开,维护一些什么样的数据? 其实我们通过观察,很快可以发现,你维护被删的数比维护所有的数轻松多了(不管是空间上,还是时间上)。所以我们就可以从这方面想!(其实我一开始的思路,因为这道题我已经看过很久了,一直想写,毕竟是白书里面的一道例题嘛!一开始,蒟蒻的我是打算这样的用树状数组套权值线段树,并且是维护所有的数,我发现空间不够我开的(或许是我太弱,有大神的话请指教一下。)。好像是因为我维护所有的数才错了,(而且就算要维护,也是动态开点第维护)仔细一想黄学长就是树状数组套权值线段树啊!太弱了我啊!too young too simple 。而且宝宝,开点的时候,一开始只开了一百万,WA掉了,后来看了黄学长开了五百万,自己再仔细一想,logN = 17 , 因为是树套树所以是 logN的平方, 还要乘上操作的次数M, 所以………………,我又错了。加油了多积累经验)。

这是黄学长的树状数组套权值线段树(当然,为了不当简单的做一条源程狗,我自己敲了一遍,并做了修改)

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #define rep(i,j,k) for(int i = j; i <= k; i++)
  5 #define down(i,j,k) for(int i = j; i >= k; i--)
  6 #define lc c[k][0]
  7 #define rc c[k][1]
  8 #define N 5000005
  9 #define ll long long
 10 #define maxn 102333
 11 using namespace std;
 12
 13 int t[maxn], c[N][2], root[maxn], sum[N], a1[maxn], a2[maxn], pos[maxn], num[maxn];
 14 int A[30], B[30];
 15
 16 int cnt = 0, n, m;
 17 int read()
 18 {
 19     int s = 0, t = 1; char c = getchar();
 20     while( !isdigit(c) ){
 21         if( c == ‘-‘ ) t = -1; c = getchar();
 22     }
 23     while( isdigit(c) ){
 24         s = s * 10 + c - ‘0‘; c = getchar();
 25     }
 26     return s * t;
 27 }
 28 int lower(int x) {return x & (-x); }
 29
 30 int get_num(int x)
 31 {
 32     int ans = 0;
 33     for( ; x; x -= lower(x))  ans += t[x];
 34     return ans;
 35 }
 36
 37 void update(int&k,int l,int r,int num)
 38 {
 39     if( !k ) k = ++cnt;
 40     sum[k]++;
 41     if( l == r ) return;
 42     int mid = (l+r)>>1;
 43     if( mid < num ) update(rc,mid+1,r,num);
 44     else update(lc,l,mid,num);
 45 }
 46
 47 ll get_more(int x,int y,int num)
 48 {
 49     A[0] = B[0] = 0; x--; ll tmp = 0;
 50     for( ; x; x -= lower(x) ) A[++A[0]] = root[x];
 51     for( ; y; y -= lower(y) ) B[++B[0]] = root[y];
 52     int l = 1, r = n;
 53     while( l != r ){
 54         int mid = (l+r)>>1;
 55         if( num <= mid ) {
 56             rep(i,1,A[0]) tmp -= sum[c[A[i]][1]];
 57             rep(i,1,B[0]) tmp += sum[c[B[i]][1]];
 58             rep(i,1,A[0]) A[i] = c[A[i]][0];
 59             rep(i,1,B[0]) B[i] = c[B[i]][0];
 60             r = mid;
 61         }
 62         else {
 63             rep(i,1,A[0]) A[i] = c[A[i]][1];
 64             rep(i,1,B[0]) B[i] = c[B[i]][1];
 65             l = mid+1;
 66         }
 67     }
 68     return tmp;
 69 }
 70
 71 ll get_less(int x,int y,int num)
 72 {
 73     A[0] = B[0] = 0; x--; ll tmp = 0;
 74     for( ; x; x -= lower(x)) A[++A[0]] = root[x];
 75     for( ; y; y -= lower(y)) B[++B[0]] = root[y];
 76     int l = 1, r = n;
 77     while( l != r ){
 78         int mid = (l+r)>>1;
 79         if( num > mid ){
 80             rep(i,1,A[0]) tmp -= sum[c[A[i]][0]];
 81             rep(i,1,B[0]) tmp += sum[c[B[i]][0]];
 82             rep(i,1,A[0]) A[i] = c[A[i]][1];
 83             rep(i,1,B[0]) B[i] = c[B[i]][1];
 84             l = mid + 1;
 85         }
 86         else {
 87             rep(i,1,A[0]) A[i] = c[A[i]][0];
 88             rep(i,1,B[0]) B[i] = c[B[i]][0];
 89             r = mid;
 90         }
 91     }
 92     return tmp;
 93 }
 94
 95 int main()
 96 {
 97     n = read(), m = read(); ll ans = 0;
 98     rep(i,1,n){
 99         num[i] = read(); pos[num[i]] = i;
100         a1[i] = get_num(n)-get_num(num[i]-1);
101         ans += a1[i];
102         for(int x = num[i]; x <= n; x += lower(x) ) t[x]++;
103     }
104     memset(t,0,sizeof(t));
105     down(i,n,1){
106         int x = num[i];
107         a2[i] = get_num(x-1);
108         for( ; x <= n; x += lower(x) ) t[x]++;
109     }
110     rep(i,1,m){
111         printf("%lld\n", ans);
112         int x = read(); int po = pos[x];
113         ans -= (a1[po] + a2[po] - get_more(1,po-1,x) - get_less(po+1,n,x));
114         for( ; po <= n; po += lower(po) ) update(root[po],1,n,x);
115     }
116     return 0;
117 }

据翻博客,有人分块A掉了,我不信,怎么可能呢?可是这怎么知道呢?还是还要敲的。很明显,以比树套树的代码慢一倍的代价(大约),A掉了。哈哈!到现在好像还真的没怎么看过分块A不掉的题(不过肯定是有的,是蒟蒻写的题太少,蒟蒻写的题分块几乎都能写),但是写这个不利于自身水平的提高啊!(不到迫不得已,还是少写分块吧!)

 1 #include<vector>
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #include<cstring>
 6 #define ll long long
 7 #define rep(i,j,k) for(int i = j; i <= k; i++)
 8 #define down(i,j,k) for(int i = j; i >= k; i--)
 9 #define maxn 323
10 #define inf 0x7fffffff
11 using namespace std;
12
13 vector<int> s[maxn];
14 int pos[100233], v[100233], t[100233];
15 bool vis[100233];
16 int n, m;
17
18 int lower(int x ) { return x & (-x); }
19 int getans(int x)
20 {
21     int ans = 0; for(; x; x -= lower(x) ) ans += t[x];
22     return ans;
23 }
24
25 int read()
26 {
27     int s = 0, t = 1; char c = getchar();
28     while( !isdigit(c) ){
29         if( c == ‘-‘ ) t = -1; c = getchar();
30     }
31     while( isdigit(c) ){
32         s = s * 10 + c - ‘0‘; c = getchar();
33     }
34     return s * t;
35 }
36
37 int get_max(int r,int num)
38 {
39     int last = r / maxn; int tmp = 0;
40     rep(i,0,last-1) {
41         tmp += (s[i].size()-1-(upper_bound(s[i].begin(),s[i].end(),num)-s[i].begin()));
42     }
43     int begin = last*maxn;
44     rep(i,begin,r){
45         if( !vis[i] && v[i] > num ) tmp++;
46     }
47     //cout<<" a "<<tmp<<" ";
48     return tmp;
49 }
50
51 int get_min(int l,int num)
52 {
53     int begin = l / maxn; int tmp = 0;
54     rep(i,begin+1,n/maxn){
55         tmp += (lower_bound(s[i].begin(),s[i].end(),num)-s[i].begin());
56     }
57     int last = (begin+1)*maxn-1;
58     rep(i,l,min(last,n)){
59     //    cout<<l+1<<" "<<last<<endl;
60     //        cout<<"k "<<i<<endl;
61         if( !vis[i] && v[i] < num ) tmp++;
62     }
63 //    cout<<" i "<<tmp<<" ";
64     return tmp;
65 }
66
67 void update(int pos,int num)
68 {
69     int x = pos / maxn;
70 //    cout<<"U "<<pos<<endl;
71     s[x].erase(lower_bound(s[x].begin(),s[x].end(),num));
72     vis[pos] = 1;
73 }
74
75 int main()
76 {
77     n = read(), m = read(); ll ans = 0;
78     rep(i,0,maxn) s[i].push_back(inf);
79     rep(i,1,n){
80         v[i] = read(); pos[v[i]] = i; s[i/maxn].insert(lower_bound(s[i/maxn].begin(),s[i/maxn].end(),v[i]),v[i]);
81         ans += (getans(n)-getans(v[i]-1));
82         for(int x = v[i]; x <= n ; x += lower(x) ) t[x]++;
83     }
84     memset(t,0,sizeof(t));
85     rep(i,1,m){
86         printf("%lld\n", ans);
87         int x = read(), po = pos[x];
88         ans -= (get_max(po-1,x)+get_min(po+1,x));
89         update(po,x);
90         //cout<<endl;
91     }
92     return 0;
93 }

据说此题还有 cdq分治写法,我也orz一下。(这个明天再写了)

时间: 2024-10-15 20:25:22

BZOJ3295 动态逆序对 树套树, 树状数组套线段树(主席树)的相关文章

bzoj3295动态逆序对

Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 5362  Solved: 1814[Submit][Status][Discuss] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数

HYSBZ 1901 Dynamic Rankings 树状数组套主席树

ZOJ上面这题内存限制太严格,裸的树套树主席树搞法过不去,BZOJ上面这个放的比较松,可以过. 其实就是利用树状数组维护n颗主席树,然后利用前缀和性质求解第k大. #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include

BZOJ1901 Zju2112 Dynamic Rankings 【树状数组套主席树】

题目 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改 变后的a继续回答上面的问题. 输入格式 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000). 分别表示序列的长度和指令的个数. 第二行有n个数,表示a[1],a[2]--a[n],这些数都小于10^9. 接下来的m

CF785CAnton and Permutation(分块 动态逆序对)

Anton likes permutations, especially he likes to permute their elements. Note that a permutation of n elements is a sequence of numbers {a1, a2, ..., an}, in which every number from 1 to n appears exactly once. One day Anton got a new permutation and

[bzoj3295][Cqoi2011][动态逆序对] (树套树)

Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个数. Sample Input 5 4 1 5 3

BZOJ3295 CQOI2011 动态逆序对 树状数组套线段树

离线倒着做,每次加入一个节点后新增的逆序对数量就是其左边大于它的数的个数(左边数的总数-左边小于它的数的个数)+右边小于它的数的个数 用树状数组维护求和,对于树状数组中每个节点v所对应的区间线段树维护区间[l,r]中大于v的数的个数. 最后唯一的问题就是指针版线段树MLE-- #include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <alg

Bestcoder7(1004)hdu4988(经典问题:树状数组套treap求解动态逆序对)

Little Pony and Boast Busters Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 83    Accepted Submission(s): 32 Problem Description "I hereby challenge you, Ponyvillians: anything you can do

[Luogu P3157][CQOI2011]动态逆序对 (树套树)

题面 传送门:[CQOI2011]动态逆序对 Solution 一开始我看到pty巨神写这套题的时候,第一眼还以为是个SB题:这不直接开倒车线段树统计就完成了吗? 然后冷静思考了一分钟,猛然发现单纯的线段树并不能解决这个问题,好像还要在外面再套上一颗树. 这就很shit了.你问我资磁不资磁树套树,我是不资磁的,树套树是暴力数据结构,我能资磁吗? 很不幸,昨天现实狠狠地打了我一脸:时间不够开新坑的,不切题又浑身难受,找了半天题,还是把这道题拉了出来(哈,真香) 不扯淡了,这题还是很显然的. 考虑开

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的