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
5 4
4 5
2 4
2 5
2 2
1
4
3
3
2 1
2 1
1
6 7
1 4
3 5
2 3
3 3
3 6
2 1
5 1
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 = + ;
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-)/block+;
for (int i=;i<=num;i++){
L[i]=(i-)*block+;
R[i]=i*block;
} R[num]=n; //修改细节
for(int i=;i<=n;i++)
belong[i]=(i-)/block + ;
for(int i=;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 ;
int ans=;
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]+;i<=belong[r]-;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=;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+,v-,a[u]);//期间比左边小的
int t2=v--u-+-t1;//期间比左边大的
ans-=t1; ans+=t2;
t1=query(u+,v-,a[v]);
t2=v--u-+-t1;
ans+=t1; ans-=t2;
if(a[u]<a[v])++ans;
else ans--;
printf("%lld\n",ans);
update(u,v);
}
return ;
}
CF785CAnton and Permutation(分块 动态逆序对)的更多相关文章
- Bzoj 3295: [Cqoi2011]动态逆序对 分块,树状数组,逆序对
3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2886 Solved: 924[Submit][Stat ...
- BZOJ 3295: [Cqoi2011]动态逆序对
3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 3865 Solved: 1298[Submit][Sta ...
- 【Luogu1393】动态逆序对(CDQ分治)
[Luogu1393]动态逆序对(CDQ分治) 题面 题目描述 对于给定的一段正整数序列,我们定义它的逆序对的个数为序列中ai>aj且i < j的有序对(i,j)的个数.你需要计算出一个序 ...
- 【BZOJ3295】动态逆序对(线段树,树状数组)
[BZOJ3295]动态逆序对(线段树,树状数组) 题面 Description 对于序列A,它的逆序对数定义为满足iAj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的 ...
- bzoj3295[Cqoi2011]动态逆序对 树套树
3295: [Cqoi2011]动态逆序对 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 5987 Solved: 2080[Submit][Sta ...
- cdq分治(hdu 5618 Jam's problem again[陌上花开]、CQOI 2011 动态逆序对、hdu 4742 Pinball Game、hdu 4456 Crowd、[HEOI2016/TJOI2016]序列、[NOI2007]货币兑换 )
hdu 5618 Jam's problem again #include <bits/stdc++.h> #define MAXN 100010 using namespace std; ...
- P3157 [CQOI2011]动态逆序对(树状数组套线段树)
P3157 [CQOI2011]动态逆序对 树状数组套线段树 静态逆序对咋做?树状数组(别管归并QWQ) 然鹅动态的咋做? 我们考虑每次删除一个元素. 减去的就是与这个元素有关的逆序对数,介个可以预处 ...
- P3157 [CQOI2011]动态逆序对
P3157 [CQOI2011]动态逆序对 https://www.luogu.org/problemnew/show/P3157 题目描述 对于序列A,它的逆序对数定义为满足i<j,且Ai&g ...
- 2018.07.01 BZOJ3295: [Cqoi2011]动态逆序对(带修主席树)
3295: [Cqoi2011]动态逆序对 **Time Limit: 10 Sec Memory Limit: 128 MB Description 对于序列A,它的逆序对数定义为满足i<j& ...
随机推荐
- 面试题 15:链表中倒数第 k 个结点
面试题 15:链表中倒数第 k 个结点 题目:输入一个链表,输出该链表中倒数第 k 个结点.为了符合大多数人的习惯, 本题从 1 开始计数,即链表的尾结点是倒数第一个结点.例如一个有 6 个结点的 链 ...
- 软件工程第3次作业——Visual Studio 2017下针对代码覆盖率的C/C++单元测试
本项目Github地址(同时包括两个作业项目): Assignment03 -- https://github.com/Oberon-Zheng/SoftwareEngineeringAssignme ...
- 你要的最后一个字符就在下面这个字符串里,这个字符是下面整个字符串中第一个只出现一次的字符。(比如,串是abaccdeff,那么正确字符就是b了)
include "stdafx.h" #include<iostream> #include<string> using namespace std; in ...
- 度度熊有一张网格纸,但是纸上有一些点过的点,每个点都在网格点上,若把网格看成一个坐标轴平行于网格线的坐标系的话,每个点可以用一对整数x,y来表示。度度熊必须沿着网格线画一个正方形,使所有点在正方形的内部或者边界。然后把这个正方形剪下来。问剪掉正方形的最小面积是多少。
// ConsoleApplication10.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream& ...
- A charge WIFI point base on airbase-ng+dhcp+lamp+wiwiz
Make wifi as a hot point Make a script echo $0 $1 case $1 in "start") sleep 1 ifconfig wla ...
- 修改zend studio字符集
zend studio是一款编辑PHP的很好的工具,但是它的默认字符集是GBK,如何修改成UTF-8呢? 一.修改整个编辑器的编码 其实很简单,如果你做的每一个项目都是固定的某一个字符集(如UTF-8 ...
- 《Java设计模式》之构建者模式
概述: 构造者模式(Builder Pattern):构造者模式将一个复杂对象的构造过程和它的表现层分离开来.使得相同的构建过程能够创建不同的表示,又称为生成器模式. Bu ...
- mysql 查看触发器,删除触发器
1. 查看所有触发器 2. 根据触发器名称看下相关触发器信息 3. 查看所有触发器 另一种查询触发器命令: show triggers; 删除触发器命令: drop trigger trigg ...
- 2017-2018-1 20179209《Linux内核原理与分析》第三周作业
一.函数调用堆栈 存储程序.函数调用堆栈(高级语言起点)和中断机制是计算机工作的三大法宝.其中函数调用堆栈是本次学习的重点.先介绍一些基本的知识点: 1.ebp 在C语言中用作记录当前函数调用的基址: ...
- Grunt学习笔记【6】---- grunt-contrib-requirejs插件详解
本文主要讲如何使用Grunt实现RequireJS文件压缩. 一 说明 ES6出来前,RequireJS是JavaScript模块化最常用的方式之一.对于使用RequireJS构建的项目,要实现打包压 ...