P1975 [国家集训队]排队 分块的伪代码

题目链接
该题是由分块做的,蒟蒻也只能用分块去做了,树套树代码太长了啊呜呜呜。
首先我们先对逆序对做一个分析,课本(线性代数工程学,同济大学) 说,对于n个不同的元素,先规定个元素之间有以一个标准次序(例如n个不同的自然数,可规定由小到大为标准次序), 于是在这n个元素的任意排列中,当某一对元素的先后次序与标准次序不同时,就说它构成1个逆序,一个排列中所有逆序的总数叫做这个排列的逆序数。

然后我们进行的在线操作时交换任意两个元素,首先我们进行分析, 这两个元素的下标如果为x , y的话,我们其实对于[1 , x- 1] , [y + 1 , n]这两个区间是没有任何影响的,可以自己模拟一遍,而对其内部来说我们需要考虑内部与两个元素之间的关系。
在这里插入图片描述

分块的细节操作正在更新ing

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>

using namespace std ;

const int N = 1e5 + 10 ;

int read()
{
	int res = 0 , flag = 1  ;
	char c = getchar() ;
	while(!isdigit(c))
	{
		if(c == '-') flag = -1 ;
		c = getchar() ;
	}
	while(isdigit(c))
	{
		res = (res << 1) + (res << 3) + (c ^ 48) ;
		c = getchar() ;
	}
	return res * flag ;
}
int n , m , t , ans = 0 ;
int len , pos[N] , L[N] , R[N] ;
int a[N] , b[N] , tmp[N] ;

int get(int x)
{
	return x / len ;
}

void maintain(int t)
{
	for(int i = L[t] ; i <= R[t] ; i ++)
		b[i] = a[i] ;
	sort(b + L[t] , b + R[t] + 1) ;	
}
void init()
{
    memset(L , 0x3f, sizeof L) ;
	for(int i = 1 ; i <= n ; i ++)
	{
		pos[i] = get(i) ;
		t = pos[i] ;
		L[pos[i]] = min(L[pos[i]] , i) ;
		R[pos[i]] = max(R[pos[i]] , i) ; 
	} 
	for(int i = 1 ; i <= t ; i ++)
		maintain(t) ;	
}

void mergesort(int l , int r)
{
	if(l >= r) return ;
	int mid = l + r >> 1 ;
	mergesort(l , mid) ;
	mergesort(mid + 1 , r) ;
	int i = l , j = mid + 1 ;
	int cnt = 0 ;
	while(i <= mid && j <= r)
	{
		if(b[i] <= b[j]) tmp[++ cnt] = b[i ++] ;
		else tmp[++ cnt] = b[j ++] , ans += mid - i + 1 ;
	}
	while(i <= mid)
		tmp[++ cnt] = b[i ++] ;
	while(j <= r)
		tmp[++ cnt] = b[j ++] ;
	for(int i = 1 ; i <= cnt; i ++)
		b[l + i - 1] = tmp[i] ;
}

int query(int l , int r , int val) 
{
	int res = 0 ;
	if(l > r) return 0 ;
	if(pos[l] == pos[r])
	{
		for(int i = l ; i <= r ; i ++)
			if(a[i] > val) 
				res ++ ;
		return res ;	
	}
	for(int i = l ; i <= R[pos[l]] ; i ++)
		if(a[i] > val)
			res ++ ;
	for(int j = r ; j >= L[pos[r]] ; j --)
		if(a[j] > val)
			res ++ ;
	for(int i = pos[l] + 1 ; i <= pos[r] - 1 ; i ++)
		res += R[i] - (lower_bound(b + L[i] , b + R[i] + 1 , val + 1) - b) + 1 ;
	return res ;
}
int main(void)
{
	n = read();
	len = sqrt(n) ;
	for(int i = 1 ; i <= n ; i ++)
		a[i] = read() ;
	for(int i = 1 ; i <= n ; i ++)
	    b[i] = a[i] ;
	mergesort(1 , n) ;
	printf("%d\n" , ans) ;
	init() ;
	m = read() ;
	for(int i = 1 ; i <= m ; i ++)
	{
		int x , y ;
		x = read() , y = read() ;
		int ans_a = query(x + 1 , y - 1 , a[x]) ;
		int ans_b = query(x + 1 , y - 1 , a[y]) ;
//		cout << ans_a << " " << ans_b << endl ;
//		cout << (a - n + a) + (n - b - b) << endl ;
        ans += 2 * ans_a - 2 * ans_b ;
        if(a[x] < a[y]) ans ++ ;
        else ans -- ;
		printf("%d\n" , ans) ;
		swap(a[x] , a[y]) ;
		maintain(pos[x]) ;
		if(pos[x] != pos[y])
			maintain(pos[y]) ;
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值