码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-01-05 22:04:04      阅读:214      评论:0      收藏:0      [点我收藏+]

标签:style   有序数组   div   note   red   spec   using   sub   离线   

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 started to play with it. He does the following operation q times: he takes two elements of the permutation and swaps these elements. After each operation he asks his friend Vanya, how many inversions there are in the new permutation. The number of inversions in a permutation is the number of distinct pairs (i, j) such that 1 ≤ i < j ≤ n and ai > aj.

Vanya is tired of answering Anton‘s silly questions. So he asked you to write a program that would answer these questions instead of him.

Initially Anton‘s permutation was {1, 2, ..., n}, that is ai = i for all i such that 1 ≤ i ≤ n.

Input

The first line of the input contains two integers n and q (1 ≤ n ≤ 200 000, 1 ≤ q ≤ 50 000) — the length of the permutation and the number of operations that Anton does.

Each of the following q lines of the input contains two integers li and ri (1 ≤ li, ri ≤ n) — the indices of elements that Anton swaps during the i-th operation. Note that indices of elements that Anton swaps during the i-th operation can coincide. Elements in the permutation are numbered starting with one.

Output

Output q lines. The i-th line of the output is the number of inversions in the Anton‘s permutation after the i-th operation.

Example

Input
5 4
4 5
2 4
2 5
2 2
Output
1
4
3
3
Input
2 1
2 1
Output
1
Input
6 7
1 4
3 5
2 3
3 3
3 6
2 1
5 1
Output
5
6
7
7
10
11
8

Note

Consider the first sample.

After the first Anton‘s operation the permutation will be {1, 2, 3, 5, 4}. There is only one inversion in it: (4, 5).

After the second Anton‘s operation the permutation will be {1, 5, 3, 2, 4}. There are four inversions: (2, 3), (2, 4), (2, 5) and (3, 4).

After the third Anton‘s operation the permutation will be {1, 4, 3, 2, 5}. There are three inversions: (2, 3), (2, 4) and (3, 4).

After the fourth Anton‘s operation the permutation doesn‘t change, so there are still three inversions.

 

题意:

初始数列,a[]为顺序排列。问每次交换u,v两个位置的数字后,逆序对数量。

 

由于数状数组解决逆序对是离线操作,不支持交换操作(就我所知是如此)。反正不好快速查询u,v位置的数和之间的数大小关系。

所以用分块乱搞,如果u,v距离不远,暴力即可,如果太远,可以用分块好的有序数组快速得到排名关系。每一次操作O(lg+sqrt)。

感觉不难实现,而且马上打CF了,所以难得写一遍了。

 不过有序vector的删除和加入以前倒是没有实现过,get。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<vector>
#define ps push_back
#define Siz(x) (int)x.size()
using namespace std;
typedef long long LL;
LL ans = 0LL;
const int maxn = 200000 + 7;
int n,q;                 //n个数,m个操作 
int num;                 //num个块 
int block;               // 块的长度 
int L[maxn], R[maxn];    //每个块的左右边界 
int a[maxn];             //n个数,用与单个比较 
int belong[maxn];        //位置属于哪一块 
vector<int> bit[maxn];   //每个块,用于lower_bound快速找个数。 
void init(){
    block=sqrt(n);
    num=(n-1)/block+1;
    for (int i=1;i<=num;i++){
       L[i]=(i-1)*block+1;
       R[i]=i*block;
    }  R[num]=n;         //修改细节 
    for(int i=1;i<=n;i++)   
      belong[i]=(i-1)/block + 1;
    for(int i=1;i<=num;i++)
      for (int j=L[i];j<=R[i];j++)
        bit[i].ps(j);    //每一块的有序序列 
}
int query(int l,int r,int v){
    if (l>r) return 0;
    int ans=0;
    if(belong[l]==belong[r]){
        for(int i=l;i<=r;++i)
           if(a[i]<v) ++ans;
        return ans;
    }
    int id=belong[l];
    for(int i=l;i<=R[id];++i){
        if(a[i]<v) ans++;
    }
    for(int i=belong[l]+1;i<=belong[r]-1;i++){
        int p2=lower_bound(bit[i].begin(),bit[i].end(),v)-bit[i].begin();
        ans+=p2;
    }
    id=belong[r];
    for(int i=L[id];i<=r;i++){
        if(a[i]<v) ans++;
    }
    return ans;
}
void update(int l,int r){
    int uu=a[l];
    int vv=a[r];
    int id=belong[l];
    bit[id].erase(lower_bound(bit[id].begin(),bit[id].end(),uu));//删去。 
    bit[id].insert(upper_bound(bit[id].begin(),bit[id].end(),vv),vv);//加入 
    id = belong[r];
    bit[id].erase(lower_bound(bit[id].begin(),bit[id].end(),vv));
    bit[id].insert(upper_bound(bit[id].begin(),bit[id].end(),uu),uu);
    swap(a[l],a[r]);
}
int main(){
    scanf("%d %d",&n, &q);
    for (int i=1;i<=n;i++) a[i] = i;
    init();
    while(q--){
        int u,v;
        scanf("%d%d",&u,&v);
        if(u==v){
            printf("%lld\n",ans);
            continue;
        }
        if(u>v) swap(u,v);
        int t1=query(u+1,v-1,a[u]);//期间比左边小的 
        int t2=v-1-u-1+1-t1;//期间比左边大的 
        ans-=t1; ans+=t2;      
        t1=query(u+1,v-1,a[v]);
        t2=v-1-u-1+1-t1;
        ans+=t1; ans-=t2;
        if(a[u]<a[v])++ans;
        else ans--;
        printf("%lld\n",ans);
        update(u,v);
    }
    return 0;
}

 

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

标签:style   有序数组   div   note   red   spec   using   sub   离线   

原文地址:https://www.cnblogs.com/hua-dong/p/8206548.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!