Description
Hercier作为一位喜爱Hatsune Miku的OIer,痛下决心,将Vocaloid买回了家。打开之后,你发现界面是一个长为n的序列,代表音调,并形成了全排列。你看不懂日语,经过多次尝试,你只会用一个按钮:将一段区间按升序排序。不理解音乐的Hercier决定写一个脚本,进行m次操作,每次对一段区间进行操作。可惜Hercier不会写脚本,他找到了在机房里的你,请你模拟出最后的结果。
Input
Output
Data Constraint
Solution
我们知道冒泡排序的交换次数是逆序对个数;而冒泡排序的交换方法是交换相邻逆序对。 既然这样,可以将本题的排序变为逆序对排序。我们记录下所有的相邻逆序对,用一棵线段树存储一段区间中最左边的相邻逆序对。对于询问
(
l
i
,
r
i
)
(l_i,r_i)
( l i , r i ) ,我们不断地寻找区间
[
l
i
,
r
i
]
[l_i,r_i]
[ l i , r i ] 中最靠左的相邻逆序对,若找得到,则交换之,同时维护线段树。 分析一波时间复杂度。我们至多进行
n
2
n^2
n 2 次交换,每次交换前需要
log
2
n
\log_2n
log 2 n 的查询时间,交换后需要
log
2
n
\log_2n
log 2 n 的维护时间。因此,总时间复杂度为
O
(
(
n
2
+
m
)
log
2
n
)
O((n^2+m)\log_2n)
O ( ( n 2 + m ) log 2 n ) 。
Code
#include <bits/stdc++.h>
#define A v<<1
#define B A|1
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int N= 1501 ;
int i, n, m, L, R, a[ N] , px, pv, t[ N* N] , x, y, pos;
inline int min ( int x, int y) { return x< y? x: y; }
void read ( int & x)
{
char ch= getchar ( ) ; x= 0 ;
for ( ; ! isdigit ( ch) ; ch= getchar ( ) ) ;
for ( ; isdigit ( ch) ; ch= getchar ( ) ) x= ( x<< 3 ) + ( x<< 1 ) + ( ch^ 48 ) ;
}
void modify ( int v, int l, int r)
{
if ( l== r) { t[ v] = pv; return ; }
int mid= l+ r>> 1 ;
px<= mid ? modify ( A, l, mid) : modify ( B, mid+ 1 , r) ;
t[ v] = min ( t[ A] , t[ B] ) ;
}
void ins ( int i)
{
px= i; pv= ( a[ i] > a[ i+ 1 ] ? i: n+ 1 ) ;
modify ( 1 , 1 , n) ;
}
int query ( int v, int l, int r)
{
if ( x<= l&& r<= y) return t[ v] ;
if ( y< l|| r< x) return n+ 1 ;
int mid= l+ r>> 1 ;
return min ( query ( A, l, mid) , query ( B, mid+ 1 , r) ) ;
}
int main ( )
{
freopen ( "miku.in" , "r" , stdin ) ;
freopen ( "miku.out" , "w" , stdout ) ;
read ( n) ; read ( m) ; read ( L) ; read ( R) ;
memset ( t, 127 , sizeof t) ;
fo ( i, 1 , n)
{
read ( a[ i] ) ;
if ( i> 1 ) ins ( i- 1 ) ;
}
fo ( i, 1 , m)
{
read ( x) ; read ( y) ;
rep: pos= query ( 1 , 1 , n) ;
if ( pos>= y) continue ;
swap ( a[ pos] , a[ pos+ 1 ] ) ;
if ( pos> 1 ) ins ( pos- 1 ) ;
ins ( pos) ; ins ( pos+ 1 ) ;
goto rep;
}
fo ( i, L, R) printf ( "%d " , a[ i] ) ;
}