1. 程式人生 > >JZOJ5947. 【NOIP2018模擬11.02】初音未來(miku)

JZOJ5947. 【NOIP2018模擬11.02】初音未來(miku)

Description

Hercier作為一位喜愛Hatsune Miku的OIer,痛下決心,將Vocaloid買回了家。開啟之後,你發現介面是一個長為n的序列,代表音調,並形成了全排列。你看不懂日語,經過多次嘗試,你只會用一個按鈕:將一段區間按升序排序。不理解音樂的Hercier決定寫一個指令碼,進行m次操作,每次對一段區間進行操作。可惜Hercier不會寫指令碼,他找到了在機房裡的你,請你模擬出最後的結果。
在這裡插入圖片描述

題解

這裡的m非常的大,而且n很小。
對於70%,是很簡單的,直接二分一個答案,
然後變成0/1的形式,用線段樹來維護1的個數,
排序就直接區間修改。
而對於100%就不能這樣做了。
對於一個任意的全排列,它的逆序對個數,是 n

2 n^2 級別的,
而將任意一個全排列排序交換的個數就是逆序對個數,
也就是說最多的有效交換隻有不到 n 2 n^2 次。
用一個線段樹維護一下這個位置與前面的位置是否構成逆序對,
如果有就直接交換,
這樣排序的次數就非常少了。

code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
#define ls (x<<1)
#define rs (x<<1|1)
#define M ((l+r)>>1)
using namespace std;
int n,a[2003],m,stl,str,L,R;
int opl,opr,ops,id,z[13],lim;
bool bz[8003],s[8003];
char ch;
void read(int&n)
{
	for(ch=getchar();ch<'0'|| ch>'9';ch=getchar());
	for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
}
void build(int x,int l,int r)
{
	bz[x]=0;
	if(l==r)
	{
		s[x]=(a[l]>a[l+1]);
		return;
	}
	int m=M;
	build(ls,l,m);
	build(rs,m+1,r);
	s[x]=s[ls]|s[rs];
}
void down(int x)
{
	if(bz[x])bz[ls]=bz[rs]=1,s[ls]=s[rs]=bz[x]=0;
}
void solve(int x,int l,int r)
{
	down(x);
	if(l==r)
	{
		for(int j=l;j>=L && a[j]>a[j+1];j--)
			for(int i=j;i<R && a[i]>a[i+1];i++)swap(a[i],a[i+1]);
		return;
	}
	int m=M;
	if(s[rs])solve(rs,m+1,r);
	if(s[ls])solve(ls,l,m);
}
void find(int x,int l,int r)
{
	down(x);
	if(s[x]==0)return;
	if(opl<=l && r<=opr)
	{
		solve(x,l,r);
		return;
	}
	int m=M;
	if(m<opr)find(rs,m+1,r);
	if(opl<=m)find(ls,l,m);
}
void work(int x,int l,int r)
{
	down(x);
	if(s[x]==0)return;
	if(opl<=l && r<=opr)
	{
		bz[x]=1;s[x]=0;
		return;
	}
	int m=M;
	if(opl<=m)work(ls,l,m);
	if(m<opr)work(rs,m+1,r);
	s[x]=s[ls]|s[rs];
}
void ins(int x,int l,int r)
{
	down(x);
	if(l==r)
	{
		s[x]=1;
		return;
	}
	int m=M;
	if(opl<=m)ins(ls,l,m);else ins(rs,m+1,r);
	s[x]=s[ls]|s[rs];
}
int main()
{
	freopen("miku.in","r",stdin);
	freopen("miku.out","w",stdout);
	read(n);read(m);read(stl);read(str);
	for(int i=1;i<=n;i++)read(a[i]);a[n+1]=n+1;
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		read(L);read(R);
		opl=L;opr=R;
		find(1,1,n);
		work(1,1,n);
		if(a[L-1]>a[L])opl=L-1,ins(1,1,n);
		if(a[R]>a[R+1])opl=R,ins(1,1,n);
	}
	for(int i=stl;i<=str;i++)printf("%d ",a[i]);
}