1. 程式人生 > 其它 >CF1592B Hemose Shopping 題解

CF1592B Hemose Shopping 題解

題目傳送門
題目大意:有一個長度為 \(n\) 的序列 \(a\) 和數字 \(x\) ,我們每次可以選定兩個數 \(i,j\) 滿足 \(x\le |i-j|\) ,然後交換 \(a_i,a_j\) 。請問經過若干次操作是否能讓序列 \(a\) 遞增。
題目解析:
我們發現當 \(x\le \lfloor \frac{n}{2} \rfloor\) 的時候顯然是成立的,因為我們可以通過第三個數字造作三次來交換任意兩個數字。
當然如果序列 \(a\) 是遞增的話肯定能滿足條件。

所以我們只要考慮 \(x>\lfloor \frac{n}{2} \rfloor\) 並且序列 \(a\) 不是單調遞增的情況。這是我們發現有序列中間一部分的序列是不能動的,而其他部分的序列是可以隨意交換的,方法類似於上面的方法的。
我們發現,這個時候序列中 \(a_{n-x+1},a_{n-x+2},\dots,a_x\)

這一段是不能移動的,而其他的都能任意交換。
所以需要不能移動的部分遞增,能任意移動的部分可以通過移動保證序列遞增就可以了。我們只需要統計能移動的部分小於等於 \(a_{n-x+1}\) 和大於等於 \(a_x\) 的數字的個數就可以了。當然注意考慮一下 \(a_{n-x+1}=a_x\) 的情況。

程式碼(去除了快讀&一堆沒用的巨集定義&標頭檔案):

int T,n,x,a[maxn];
int checkup(){
	for(RI i=1;i<n;i++) if(a[i]>a[i+1]) return 0;
	return 1;
}
int check(){
	if(x<=(n>>1)) return 1; if(x>=n) return checkup();
	RI i,cnt1=0,cnt2=0,minx=INF,maxx=-INF; x=n-x;
	for(i=x+1;i<=n-x;i++){
		maxx=max(maxx,a[i]); minx=min(minx,a[i]);
		if(i!=n-x&&a[i]>a[i+1]) return 0;
	}
	for(i=1;i<=x;i++)
		{ if(a[i]<minx) cnt1++; if(a[i]==minx) cnt2++; if(minx<a[i]&&a[i]<maxx) return 0; }
	for(i=n-x+1;i<=n;i++)
		{ if(a[i]<minx) cnt1++; if(a[i]==minx) cnt2++; if(minx<a[i]&&a[i]<maxx) return 0; }
	if(minx==maxx) return cnt1+cnt2>=x && cnt1<=x;
	return cnt1+cnt2==x;
}
int main(){
	T=read(); while(T--){
		n=read(); x=read(); for(RI i=1;i<=n;i++) a[i]=read();
		if(check()) puts("YES"); else puts("NO");
	}
	return 0;
}