Description
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删
除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数
Input
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。
以下n行每行包含一个1到n之间的正整数,即初始排列。
以下m行每行一个正整数,依次为每次删除的元素。
N<=100000 M<=50000
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)。
终于实战了一下分治(o゚v゚)ノ
先确定基本的思路:
我们只要算出出针对每个数他被删除时破坏了多少对逆序对即可
首先每个数开始时参与了多少个逆序对时很容易算出来的
那么我们只要对每个数而言计算删它之前有多少已经被删了的数是在他前面比他大,或者在他后面比他小
而这个东西就可以分治了。
比如说我们按照时间劈成两个区间
这样就只用统计左区间对右区间的影响就好了
事先用位置进行排序再处理就可以了~
具体看看码吧(终于自己做出来一道233ˋ( ° ▽、° )
1 #include2 #include 3 #include 4 #define ll long long 5 #define maxn 100005 6 #define maxm 50005 7 using namespace std; 8 int n,m; 9 int c[maxn],a[maxn],s[maxn],e[maxm],pos[maxn],t[maxm];10 ll ans;11 void add(int x,int z){ while(x<=n){c[x]+=z;x+=x&(-x);}}12 int sum(int x){ int ans=0;while(x){ans+=c[x];x-=x&(-x);}return ans;}13 int query(int l,int r){ if(l>r)return 0;return sum(r)-sum(l-1);}14 bool cmp(int x,int y){ return pos[x] r||(tx<=mid&&pos[e[tx]] =l||ty>=mid+1){27 if((ty =l&&pos[e[tx]]>pos[e[ty]]))add(e[tx--],1);28 else{s[e[ty]]-=query(1,e[ty]);ty--;}29 }30 for(int i=l;i<=mid;i++)add(e[i],-1);31 sort(e+l,e+r+1,cmp);32 }33 int main(){34 scanf("%d%d",&n,&m);35 for(int i=1;i<=n;i++)scanf("%d",&a[i]),pos[a[i]]=i;36 for(int i=1;i<=n;i++){s[a[i]]+=query(a[i]+1,n);add(a[i],1);ans+=s[a[i]];}37 memset(c,0,sizeof(c));38 for(int i=n;i>=1;i--){s[a[i]]+=sum(a[i]);add(a[i],1);}39 memset(c,0,sizeof(c));40 for(int i=1;i<=m;i++)scanf("%d",&e[i]),t[i]=e[i];41 solve(1,m);42 for(int i=1;i<=m;i++){43 printf("%lld\n",ans);44 ans-=s[t[i]];45 }46 return 0;47 }
话说这是不是有点压行啊。。。