思路就是对于每一个删除的数,统计在它之前有多少比它大,在它之后有多少比它小。
然后直接想到树套树(实际上是想不到更好的方法),然而树套树极其难写,所以发一个归并树的题解吧。
归并树是对于线段树每一个节点,维护一个从L到R的有序数组(一般用vector实现防止MLE),有点像归并排序的过程。
对于删除操作,我们沿着线段树的查询路径,一路更改遇到的vector,删除对应的数(用二分查找实现是logn),所以一次修改的消耗是log^2(n)。
查询就是线段树的查询方式,把询问区间拆成若干互不相交的子区间,在每个区间内二分查找,然后累加。
PS:因为归并树玄学的常数问题,需要开O2
#include<cstdio> #include<cstdlib> #include<vector> #define maxn 100010 #include<algorithm> #define ll long long #define IT vector<int>::iterator using namespace std; struct node{ int l,r; vector<int> v; } tree[maxn<<2]; int n,m,a[maxn],c[maxn],p[maxn],l1[maxn],l2[maxn]; int lowbit(int x){ return x&(-x); } void change(int x){ while(x<=n){ c[x]++; x+=lowbit(x); } } int query(int x){ int ans=0; while(x){ ans+=c[x]; x-=lowbit(x); } return ans; } void build(int pos,int left,int right){ int mid=left+right>>1; int CNT1 = 0 , CNT2 = 0 ; // standard recursion segment tree Tree [POS] .L = left; Tree [POS] .r = right; IF (left == right) { Tree [POS] .v.push_back ( a [left]); return ; } Build (POS << . 1 , left, MID); Build (POS << . 1 | . 1 , MID + . 1 , right); // the ordered array merge together the left and right child nodes for ( I = the IT Tree Register [<< POS . 1 ] .v.begin (); I = Tree [POS <<! . 1 ] .v.end (); I ++ ) L1 [ ++ CNT1] = *i; for(register IT i=tree[pos<<1|1].v.begin();i!=tree[pos<<1|1].v.end();i++) l2[++cnt2]=*i; int i=1,j=1; while(i<=cnt1&&j<=cnt2){ if(l1[i]<l2[j]) tree[pos].v.push_back(l1[i++]); else tree[pos].v.push_back(l2[j++]); } while(i<=cnt1) tree[pos].v.push_back(l1[i++]); while(j<=cnt2) tree[pos].v.push_back(l2[j++]); return; } void modify(int pos,int tar){ int mid=tree[pos].l+tree[pos].r>>1; IT it=lower_bound(tree[pos].v.begin(),tree[pos].v.end(),a[tar]);//找到目标数位置 tree[pos].v.erase(it);//删除 if(tree[pos].l==tree[pos].r) return; if(tar<=mid) modify(pos<<1,tar); else modify(pos<<1|1,tar); return; } int ask(int pos,intleft, int right, int key, int of the type) { // of the type said to be smaller than the key statistics or greater than the key of IT IT; int ANS = 0 ; // if the current interval is included in the inquiry interval, two assigned to the first greater than key position IF (Tree [POS] .L> left = && Tree [POS] .r <= right) { IT = upper_bound, (Tree [POS] .v.begin (), Tree [POS] .v.end () , Key); // note is upper_bound, IF (type == . 1 ) return iT-Tree [POS] .v.begin (); // count less than its behind the else return Tree [POS] .v.end ( ) -it; // in front of the statistics is bigger than its } //递归查左右子树 int mid=tree[pos].l+tree[pos].r>>1; if(left<=mid) ans+=ask(pos<<1,left,right,key,type); if(right>mid) ans+=ask(pos<<1|1,left,right,key,type); return ans; } int main(){ int x; ll tot=0; //标准读入 scanf("%d%d",&n,&m); for(register int i=1;i<=n;++i){ scanf("%d",&a[i]); p[a[i]]=i; } //树状数组处理初始逆序对 for(register int i=n;i>=1;i--){ change(a[i]); tot+=query(a[i]-1); } build(1,1,n); printf("%lld\n",tot); for(register int i=1;i<m;i++){ scanf("%d",&x); Modify ( . 1 , P [X]); // delete tot- ASK = ( . 1 , P [X] + . 1 , n-, X, . 1 ); // subtracting the contribution of the front and rear two portions tot- ASK = ( . 1 , . 1 , P [X] - . 1 , X, 2 ); the printf ( " % LLD \ n- " , TOT); } Scanf ( " % D " , & X); return 0 ; }