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 44 52 42 52 2

Output

1433

Input

2 12 1

Output

1

Input

6 71 43 52 33 33 62 15 1

Output

567710118

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;
}

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

时间: 2024-10-09 08:06:16

CF785CAnton and Permutation(分块 动态逆序对)的相关文章

Bestcoder7(1004)hdu4988(经典问题:树状数组套treap求解动态逆序对)

Little Pony and Boast Busters Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 83    Accepted Submission(s): 32 Problem Description "I hereby challenge you, Ponyvillians: anything you can do

bzoj3295动态逆序对

Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 5362  Solved: 1814[Submit][Status][Discuss] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数

[CQOI2011]动态逆序对

P1347 - [CQOI2011]动态逆序对 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数. 以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的个

bzoj3295【CQOI2011】动态逆序对

3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 3122  Solved: 986 [Submit][Status][Discuss] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数

主席树初探 &amp; bzoj 3295: [Cqoi2011] 动态逆序对 题解

[原题] 3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 778  Solved: 263 [Submit][Status] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元

【BZOJ3295】[Cqoi2011]动态逆序对 cdq分治

[BZOJ3295][Cqoi2011]动态逆序对 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数.以下n行每行包含一个1到n之间的正整数,即初始排列.以下m行每行一个正整数,依次为每次删除的元素. Output 输出包含m行,依次为删除每个元素之前,逆序对的

【BZOJ 3295】 [Cqoi2011]动态逆序对

3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1373  Solved: 465 [Submit][Status][Discuss] Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. Input 输入第一行包含两个整数n和m,即初始元素的个数

【Luogu1393】动态逆序对(CDQ分治)

[Luogu1393]动态逆序对(CDQ分治) 题面 题目描述 对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i < j的有序对(i,j)的个数.你需要计算出一个序列的逆序对组数及其删去其中的某个数的逆序对组数. 输入输出格式 输入格式: 第一行,两个数n,m,表示序列中有n个数,要删去m个数 第二行n个数,表示给定的序列. 第三行m个数,第i个数di表示要删去原序列中的第di个数. 输出格式: 一行m+1个数.第一个数表示给定序列的逆序对组数,第i+1个数表示删去第d

【算法】CDQ分治 -- 三维偏序 &amp; 动态逆序对

初次接触CDQ分治,感觉真的挺厉害的. 整体思路即分而治之,再用之前处理出来的答案统计之后的答案. 大概流程是: 对于区间 l ~ r : 1.处理 l ~mid, mid + 1 ~ r 的答案 2.分别排序规整 3.计算 l ~ mid 中每一个数对 mid + 1 ~ r 中的答案的贡献, 累加 4.得到区间l ~ r的答案 CDQ分治我一共也才做了两道题目, 就一起整理在这里了.大体都差不多,CDQ+树状数组分别维护两个维度. 1.三维偏序 #include <bits/stdc++.h