题目
洛谷题目链接
题解
在求整体的逆序对的数量时,很好办,直接用树状数组处理即可,不过在这时,我们还需要处理出一个数组pa[]pa[]pa[],其中pa[i]pa[i]pa[i]代表在区间[1,i)[1,i)[1,i)中满足aj>aia_j>a_iaj>ai的aja_jaj有多少个。
然后我们再利用一个新的BITBITBIT,并且倒着遍历a[]a[]a[]数组,这样我们可以处理出sa[]sa[]sa[]数组,其中sa[i]sa[i]sa[i]表示在区间(i,n](i,n](i,n]中有多少jjj使得,ai>aja_i > a_jai>aj。
我们考虑删除一个元素对答案的影响,删除一个元素以后,那么要从答案中减去这个元素原来能构成的逆序对的数量,也就是pa[i]+sa[i]pa[i]+sa[i]pa[i]+sa[i]。但是呀,这里有一个问题!就是说pa[i]pa[i]pa[i]或者是sa[i]sa[i]sa[i]中很可能包含有已经被删除掉的元素了。
解决这个问题有两个方案,第一就是我们每删除的一个元素就对所有依赖这个元素的sa[]、pa[]sa[]、pa[]sa[]、pa[]数组全部更新,这样显然不合理,遇到极端数据时间复杂度会爆炸。
还有一个解决方案就是,我们把要减去的部分容斥一下,即在已经被删除的元素里面,再找于、与aia_iai元素能构成逆序对的数量ttt。
这样的话,ans−=sa[i]+pa[i]−tans -= sa[i]+pa[i]-tans−=sa[i]+pa[i]−t。
这个思路听起来很完美,但是这个ttt要怎么找呢?
其实我们需要的是对所有被删除的元素建立一颗可修改的主席树!
正常的主席树想要修改的时候,如果修改了第iii个版本的主席树,同时要把i+1、i+2...i+1、i+2...i+1、i+2...等版本的主席树都修改,因为是主席树跟前一个版本的主席树是有依赖关系的。
我们发现树状数组跟我们的需求非常像,因为树状数组具有区间求和的性质,也具有单点修改时间复杂度O(logn)O(logn)O(logn)的优秀性质。
于是!我们可以用树状数组套权值线段树来解决这个问题。
由于空间所限,我们做线段树采用动态开点的方法。
其中树状数组里的每一个节点里面包含有一颗线段树。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
#define pr(x) cout<<#x<<":"<<x<<endl
#define int long long
const int maxn = 200007;struct segtree{int root[maxn*40];int lson[maxn*40];int rson[maxn*40];int val[maxn*40];int idx;void ins(int &rt,int l,int r,int pos,int v){if(!rt) rt = ++idx;val[rt] += v;if(l == r) return ;int mid = (l+r)/2;if(pos <= mid) ins(lson[rt],l,mid,pos,v);else ins(rson[rt],mid+1,r,pos,v);} int query(int rt,int l,int r,int ul,int ur){if(!rt || r < ul || ur < l) return 0;if(ul <= l && r <= ur) return val[rt];int mid = (l+r)/2;int a = query(lson[rt],l,mid,ul,ur);int b = query(rson[rt],mid+1,r,ul,ur);return a+b;}
}seg;struct BIT{int a[maxn];void add(int pos,int v){for(;pos < maxn;pos += pos & (-pos))a[pos] += v;}int query(int pos){int ans = 0;for(;pos;pos -= pos & (-pos))ans += a[pos];return ans;}
}b1,b2;
int pos[maxn],a[maxn];
int pa[maxn],sa[maxn];
int n,m;signed main(){long long ans = 0;cin>>n>>m;for(int i = 1;i <= n;++i){scanf("%lld",&a[i]);pos[a[i]] = i;b1.add(a[i],1);ans += pa[i] = b1.query(n) - b1.query(a[i]);}for(int i = n;i > 0;i--){sa[i] = b2.query(a[i]-1);b2.add(a[i],1);}for(int i = 1;i <= m;++i){printf("%lld\n",ans);int v;scanf("%lld",&v);int p = pos[v];p--;long long tmp = 0;for(;p;p -= p & (-p))tmp += seg.query(seg.root[p],0,100001,v+1,100001);p = pos[v];for(;p;p -= p & (-p))tmp -= seg.query(seg.root[p],0,100001,0,v-1);p = n;for(;p;p -= p & (-p))tmp += seg.query(seg.root[p],0,100001,0,v-1);ans -= sa[pos[v]] + pa[pos[v]] - tmp;p = pos[v];for(;p <= n;p += p&(-p))seg.ins(seg.root[p],0,100001,v,1);}
}