【可持久化线段树】【主席树】[BZOJ 3295]动态逆序对
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
这里可以发现对于每一个数字可以有两种做法第一种就是先建好树然后每一次删除询问在他之前有多少个比他大的,询问有多少个比他小的在他之后,然后减去(不推荐该做法)
另一种做法就是首先预处理出来左边有多少个比他大的,右边有多少个比他小的,然后每一次减去左边大于他的数量和右边小于他的数量,最后加上要减去的东西中有多少个已经被删除了这个利用树状数组套主席树就可以处理出来(找前面有多少个比他大的, 后边有多少个比他小的)然后把当前这个删除的元素加到树里面
其实就是维护的删除的元素的树
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int MAXN = 100000; struct Tree{ int ch[2], sum; }pool[MAXN*80+10]; int tot, roots[MAXN+10], n, m, val[MAXN+10], ld[MAXN+10], rx[MAXN+10], ret; int lowbit(int k){return (k&(-k));} void Insert(int &u, int l, int r, int p){ pool[++tot] = pool[u], pool[tot].sum++; u = tot; if(l == r) return ; int mid = (l + r) >> 1; if(p <= mid) Insert(pool[u].ch[0], l, mid, p); else Insert(pool[u].ch[1], mid+1, r, p); } int Que(int u, int l, int r, int h){ if(r <= h) return pool[u].sum; if(l > h) return 0; int mid = (l + r) >> 1; return Que(pool[u].ch[0], l, mid, h) + Que(pool[u].ch[1], mid+1, r, h); } int Query(int p, int v){ ret = 0; for(;p;p-=lowbit(p)) ret+=Que(roots[p], 1, n, v); return ret; } int pos[MAXN+10]; int main(){ long long tmp=0, ans=0; scanf("%d%d", &n, &m); for(int i=1;i<=n;i++){ scanf("%d", &val[i]); pos[val[i]] = i; for(int j=val[i];j;j-=lowbit(j)) ld[i]+=roots[j]; ans += 1LL * (ld[i] = i-1-ld[i]); for(int j=val[i];j<=n;j+=lowbit(j)) roots[j]++; } memset(roots, 0, sizeof roots); for(int i=n;i>=1;i--){ for(int j=val[i];j;j-=lowbit(j)) rx[i]+=roots[j]; for(int j=val[i];j<=n;j+=lowbit(j)) roots[j]++; } memset(roots, 0, sizeof roots); printf("%lld\n", ans); m--; while(m--){ scanf("%d", &tmp) , tmp = pos[tmp]; ans -= ld[tmp] + 1LL * (rx[tmp] - Query(tmp, 1000000) + 2 * Query(tmp, val[tmp]) - Query(n, val[tmp])); for(int j=tmp;j<=n;j+=lowbit(j)) Insert(roots[j], 1, n, val[tmp]); printf("%lld\n", ans); } return 0; }