牛客IOI周赛26-提高组 A. 逆序对

题面

逆序对

有一个长度为 N \tt N N 的排列 a a a,进行 M \tt M M 次操作,操作有 4 \tt 4 4 种:

  • 1 l r :交换 a l \tt a_l al a r \tt a_r ar.
  • 2 l r :翻转区间 [ l , r ] \tt [l,r] [l,r].
  • 3 l r k :将区间 [ l , r ] \tt [l,r] [l,r] 中的数向左转 k \tt k k 位.
  • 4 l r k :将区间 [ l , r ] \tt [l,r] [l,r] 中的数向右转 k \tt k k 位.

每次操作后,询问该排列是奇排列还是偶排列。

1 ≤ N ≤ 2 ⋅ 1 0 5 , 1 ≤ M ≤ 5 ⋅ 1 0 5 \tt 1\leq N\leq 2\cdot10^5,1\leq M\leq5\cdot10^5 1N21051M5105.

题解

一共三道题的比赛总是那么极端,第一题永远都是用来拉开时间差距以及淘汰过于弱小的选手的签到题。当然,情况并不绝对,有的人尽管这一道题没有做出来,但还是拿了 240 \tt240 240 的高分。

有这么一个显而易见的结论:交换排列中相邻的两个数,逆序对数一定变化 1 \tt1 1

那么所有的操作,都用交换相邻两个数的基本操作组成,就可以简化不少了。

对于第一种操作,逆序对数一定变化 1 \tt1 1 ,也就是奇偶性改变。动用行列式的知识我们也可以得到这个结论。可以这么想: a r \tt a_r ar 先移动到 a l + 1 \tt a_{l+1} al+1 的位置,与 a l \tt a_l al 交换,再移动回去。来回一趟交换次数是偶数,加上与 a l \tt a_l al 交换那一次,一定是奇数。有选手在评论区说, l = r \tt l=r l=r 时要特判……也有道理,毕竟题目条件给的是 l ≤ r \tt l\leq r lr

对于第二种操作,相当于把中心对称的点都进行一次操作一,不必过多赘述。

对于第三种操作和第四种操作,都是把一段的点移动到另一端,进行 k \tt k k 次。每移动一次,相邻交换的次数是 r − l \tt r-l rl ,那么对逆序对数的改变量就 ≡ ( r − l ) ∗ k    (  ⁣ ⁣ ⁣ ⁣ m o d    2 ) \tt\equiv(r-l)*k~~(\!\!\!\!\mod2) (rl)k  (mod2)

CODE

为体现对数据的尊重,我把操作一 l = r \tt l=r l=r 的特判加上了。

#include<map>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 500005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
int a[MAXN];
int c[MAXN];
void addc(int x,int y) {while(x<=n)c[x]+=y,x+=lowbit(x);}
int Sum(int x) {int as=0;while(x>0)as+=c[x],x-=lowbit(x);return as;}
int main() {
	n = read();m = read();
	for(int i = 1;i <= n;i ++) {
		a[i] = read();
	}
	int sum = 0;
	for(int i = n;i > 0;i --) {
		(sum += Sum(a[i]-1)) %= 2;
		addc(a[i],1);
	}
	for(int i = 1;i <= m;i ++) {
		k = read();
		if(k == 1) {
			s = read();o = read();
			if(s != o) sum ^= 1;
		}
		else if(k == 2) {
			s = read();o = read();
			int le = (o-s+1)/2;
			sum ^= (le&1);
		}
		else {
			s = read();o = read();k = read();
			int le = (k&1) * (o-s);
			sum ^= (le&1);
		}
		printf("%d\n",sum&1);
	}
	return 0;
}
posted @ 2021-06-13 15:11  DD_XYX  阅读(25)  评论(0)    收藏  举报