zoukankan      html  css  js  c++  java
  • CF785CAnton and Permutation(分块 动态逆序对)

    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
    ",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
    ",ans);
            update(u,v);
        }
        return 0;
    }
  • 相关阅读:
    Spring MVC与JAX-RS比较与分析
    JDK历史版本下载
    第六篇:为多态基类声明虚析构函数
    第五篇:明确拒绝不想编译器自动生成的拷贝构造函数和赋值运算符重载函数
    第四篇:了解 C++ 默默编写并调用的函数
    第三篇:确保对象在被使用前的初始化
    poj 2125(最小割)
    hdu 4704(费马小定理)
    hdu 4705(树形DP)
    poj 3469(网络流模版)
  • 原文地址:https://www.cnblogs.com/hua-dong/p/8206548.html
Copyright © 2011-2022 走看看