首页 > 其他分享 >CF1984G Magic Trick II 题解

CF1984G Magic Trick II 题解

时间:2024-08-09 21:57:54浏览次数:13  
标签:dots II int 题解 Trick while 操作 now alter

前记

第一篇黑题题解。难调。好写。码量不大

Description

给定一个大小为 n n n 的排列 p p p,选择一个 k k k,对 p p p 执行操作若干次使得 p i = i p_i=i pi​=i。

每次操作有两个参数 i , j i,j i,j 表示将从 p i p_i pi​ 开始的连续 k k k 个数从 p p p 中取出,再插入此时 p p p 中第 j − 1 j-1 j−1 和第 j j j 个数之间。

如, p = [ 1 , 2 , 3 , 4 , 5 ] p=[1,2,3,4,5] p=[1,2,3,4,5], k k k 取 3 3 3,依次执行操作 ( 2 , 1 ) (2,1) (2,1)、 ( 3 , 2 ) (3,2) (3,2):

将 [ 2 , 3 , 4 ] [2,3,4] [2,3,4] 取出,此时 p = [ 1 , 5 ] p=[1,5] p=[1,5],在将 [ 2 , 3 , 4 ] [2,3,4] [2,3,4] 放在第 0 0 0 个数和第 1 1 1 个数之间(即开头),操作完成后 p = [ 2 , 3 , 4 , 1 , 5 ] p=[2,3,4,1,5] p=[2,3,4,1,5]。

再执行 ( 3 , 2 ) (3,2) (3,2), p = [ 2 , 4 , 1 , 5 , 3 ] p=[2,4,1,5,3] p=[2,4,1,5,3]。

Solution

结论: k ≥ n − 3 k\ge n-3 k≥n−3。

可通过打表瞎猜得出。

实际上不一定要得出这个结论,因为题目要求最大化 k k k,所以 k k k 从 n n n 依次递减,越早能构造出越好。

下面证明并构造。

k = n k=n k=n 时

此时序列无论如何操作都不会变,所以要求 p p p 一开始就符合条件。

k = n − 1 k=n-1 k=n−1 时

此时序列每次操作等于循环移一位,要求 p p p 一开始在循环移若干位后能满足条件。

k = n − 2 k=n-2 k=n−2 时

先考虑 n n n 为奇数的情况。

每一次执行 ( 3 , 1 ) (3,1) (3,1) 相当于循环移 2 2 2 位,由于 n n n 为奇数,每个数所在下标奇偶性会改变,所以在执行 n n n 此操作以内能使序列循环移位任意长度。

  1. 先把 n o w now now(初始为 2 2 2)移到第 1 1 1 位,操作为 ( 3 , 1 ) (3,1) (3,1)

  2. 在后 n − 1 n-1 n−1 个数中不断移位使 n o w − 1 now-1 now−1 在第 n n n 位,操作为 ( 3 , 2 ) (3,2) (3,2)。

  3. 循环移一次,使得 n o w now now 接在 n o w − 1 now-1 now−1 的后面,操作为 ( 3 , 1 ) (3,1) (3,1)。

这样 n o w now now 和 n o w − 1 now-1 now−1 就有序了,并且之后不会再分开。

然后 n o w = n o w + 1 now=now+1 now=now+1,重复以上步骤直至 n o w = n + 1 now=n+1 now=n+1 时停止。

再考虑 n n n 为偶数的情况。

每一次执行 ( 3 , 1 ) (3,1) (3,1) 也相当于循环移 2 2 2 位,但无法改变下标奇偶性,所以在上面操作 1 1 1 中, n o w now now 可能无法移到第 1 1 1 位,但一定能移到第 1 1 1 或 n n n 位,因为 1 1 1 和 n n n 奇偶性不同。

如果能移到第 1 1 1 位,就同上操作,否则移到第 n n n 位,再在前 n − 1 n-1 n−1 个数中循环移位直到第 n − 1 n-1 n−1 位为 n o w − 1 now-1 now−1,操作为 ( 2 , 1 ) (2,1) (2,1),注意此时不再需要操作 3 3 3。

考虑 1 1 1 到 n − 2 n-2 n−2 已排好序,此时有六种情况:

  • n , n − 1 , 1 … n − 2 n,n-1,1\dots n-2 n,n−1,1…n−2

  • n − 1 , n , 1 … n − 2 n-1,n,1\dots n-2 n−1,n,1…n−2

  • n , 1 … n − 2 , n − 1 n,1\dots n-2,n-1 n,1…n−2,n−1

  • n − 1 , 1 … n − 2 , n n-1,1\dots n-2,n n−1,1…n−2,n

  • 1 … n − 2 , n , n − 1 1\dots n-2,n,n-1 1…n−2,n,n−1

  • 1 … n − 2 , n − 1 , n 1\dots n-2,n-1,n 1…n−2,n−1,n

第 2 2 2 种能移位成第 5 5 5 种,第 4 4 4 种通过操作 ( 2 , 1 ) (2,1) (2,1) 变为第 5 5 5 种。

另外 3 3 3 种无论如何都无法变为第 5 5 5 种。

为什么?

对于操作 ( i , j ) (i,j) (i,j),若 [ p i , p i + 1 , p i + k − 1 ] [p_i,p_{i+1},p_{i+k-1}] [pi​,pi+1​,pi+k−1​] 会与 [ p x … p y ] [p_x\dots p_y] [px​…py​] 相对位置发生变化,设原来有 a a a 对逆序对,操作后则会有 k × ( y − x + 1 ) − a k\times(y-x+1)-a k×(y−x+1)−a 对逆序对,发现逆序对数奇偶性不变,因为 k k k 为偶数, k × ( y − x + 1 ) k\times(y-x+1) k×(y−x+1) 也就变为偶数。

因此每次操作无法改变序列内逆序对个数。

若初始时 p p p 就有奇数个逆序对,则最终总会有 1 1 1 个逆序对不会被消去。

那么此时 k = n − 3 k=n-3 k=n−3 才能解决问题。

k = n − 3 k=n-3 k=n−3 时

先将 n n n 移到第 n n n 位。

这里的操作较特殊。

设 x x x 表示 n n n 所在下标。

若 x ≥ k x\ge k x≥k,则将以 n n n 为结尾的长度为 k k k 的段放在末尾即可,操作为 ( x − ( n − 3 ) + 1 , 4 ) (x-(n-3)+1,4) (x−(n−3)+1,4)。

若 x < k x<k x<k,则将以 1 1 1 为开头的长度为 k k k 的段放在末尾,操作为 ( 1 , 4 ) (1,4) (1,4),即循环移位 3 3 3 位,直到满足上面的条件并执行上面的操作。

然后使 1 … n − 1 1\dots n-1 1…n−1 在前 n − 1 n-1 n−1 位排序,可以发现,此时总长 n − 1 n-1 n−1 为奇数,且 k = n − 3 = ( n − 1 ) − 2 k=n-3=(n-1)-2 k=n−3=(n−1)−2,所以将其视为上面 n n n 为奇数的情况并做操作即可。

注意下面实现时,每次操作暴力改变是 O ( n ) O(n) O(n) 的,但实际上可以用 O ( 1 ) O(1) O(1) 的链表或其他东西维护,使其总时间复杂度降为 O ( n 2 ) O(n^2) O(n2),但由于 n 2 n^2 n2 次操作严格跑不满,所以 O ( n 3 ) O(n^3) O(n3) 也能过。

Code

#include<bits/stdc++.h>
using namespace std;
int t;
int n,k,cnt,now;
int a[1010],b[1010];
int ans[5000500][2];
void alter(int x,int y){
	int s=1;
	ans[++cnt][0]=x,ans[cnt][1]=y;
	for(int i=1;i<y;i++){
		if(s>=x&&s<=x+k) s=x+k;
		b[i]=a[s++];
	}
	for(int i=y;i<=y+k-1;i++){
		b[i]=a[x+i-y];
	}
	for(int i=y+k;i<=n;i++){
		if(s>=x&&s<=x+k) s=x+k;
		b[i]=a[s++];
	}
	for(int i=1;i<=n;i++){
		a[i]=b[i];
	}
}
int find(int x){
	for(int i=1;i<=n;i++){
		if(a[i]==x) return i;
	}
}
void print(){
	cout<<cnt<<'\n';
	for(int i=1;i<=cnt;i++){
		cout<<ans[i][0]<<" "<<ans[i][1]<<'\n';
	}
}
void solve(){
	cin>>n;
	cnt=0;
	int cnt1=0,cnt2=0;
	cin>>a[1];
	for(int i=2;i<=n;i++){
		cin>>a[i];
		if(a[i]!=a[i-1]+1) cnt2++;
		for(int j=1;j<i;j++){
			if(a[i]<a[j]) cnt1++;
		}
	}
	if(cnt2==0){
		cout<<n<<'\n'<<0<<'\n';
		return ;
	}
	if(cnt2==1){
		k=n-1;
		cout<<k<<'\n';
		while(a[1]!=1){
			alter(2,1);
		}
		print();
		return ;
	}
	if(n%2==1){
		k=n-2,now=2;
		cout<<k<<'\n';
		while(now<=n){
			while(a[1]!=now){
				alter(3,1);
			}
			while(a[n]!=now-1){
				alter(3,2);
			}
			alter(3,1);
			now++;
		}
		while(a[1]!=1){
			alter(3,1);
		}
		print();
		return ;
	}
	if(cnt1%2==0){
		k=n-2,now=2;
		cout<<k<<'\n';
		int type;
		while(now<=n){
			type=find(now)%2;
			if(type==0){
				while(a[n]!=now){
					alter(3,1);
				}
				while(a[n-1]!=now-1){
					alter(2,1);
				}
				now++;
			}else{
				while(a[1]!=now){
					alter(3,1); 
				}
				while(a[n]!=now-1){
					alter(3,2);
				}
				alter(3,1);
				now++;
			}
		}
		while(a[1]!=1){
			alter(3,1);
		}
		print();
		return ;
	}else{
		k=n-3,now=2;
		cout<<k<<'\n';
		while(a[n]!=n){
			int x=find(n);
			if(x<n-3){
				alter(1,4);
			}else{
				alter(x-(n-3)+1,4);
			}
		}
		while(now<=n-1){
			while(a[1]!=now){
				alter(3,1);
			}
			while(a[n-1]!=now-1){
				alter(3,2);
			}
			while(a[1]!=1){
				alter(3,1);
			}
			now++;
		}
		print();
		return ;
	}
	
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

标签:dots,II,int,题解,Trick,while,操作,now,alter
From: https://blog.csdn.net/m0_66438282/article/details/141071854

相关文章

  • AGC001 题解
    目录A-BBQEasyB-MysteriousLightC-ShortenDiameterA-BBQEasy先将\(2N\)个数排序,从大到小考虑,最大的数一定不会产生贡献,次大的数可以和最大的数捆绑在一起,并产生贡献,以此类推,这样的贪心正确性还是较为显然的。#include<bits/stdc++.h>#definelllonglongusin......
  • CF1999F.Expected Median 题解
    一.前言这是一道很有趣的数学题目,用到了逆元和组合数学,非常适合新手练手。二.题目大意:给出一个长度为\(n\)的\(01\)数组。找出所有长度为\(k\)的子序列的中位数,求出其和。妥妥的数学题~三.分析首先考虑对答案的贡献。很明显,只有\(1\)作为中位数时才会做出贡献,于是......
  • CF908G New Year and Original Order 题解
    CF908C定义\(S(n)\)为将\(n\)所有数位从小到大排序后得到的数,求\(\sum_{i=1}^{n}S(i)\)\(1\leqn\leq10^{700}\)看到这个题大部分人都会奔着数位\(dp\)的地方想但这个题的难度在于插入一个数后不好算贡献其实也挺好算的\(dp\)维护当前若干数字排完序......
  • CF641E Little Artem and Time Machine 题解
    题目传送门前置知识CDQ分治解法单点修改区间查询,但值域巨大,考虑离散化掉\(x\)。时刻\(t\)仍很大,考虑将其作为CDQ分治的第一维,然后套个CDQ分治即可,注意及时清空桶数组。代码CodeForces275382150#include<bits/stdc++.h>usingnamespacestd;#definelllonglon......
  • CF1209E2 Rotate Columns (hard version) 题解
    CF1209E2给定\(n\timesm\)的矩阵,可以对每一列进行若干次循环移位,求操作完成后每一行的最大值之和的最大值。\(1\leqn\leq12,1\leqm\leq2000\)这里\(m\)很大,但有一个很重要的性质这\(m\)列中只有最大的前\(n\)个会对答案产生贡献因此我们直接就把......
  • 【题解】ABC365(A~E)
    前四题30min切,然后T5死磕70min+几发小唐错,距离比赛结束还有16s交最后一发,AC了。目录A.LeapYear题目描述思路代码B.SecondBest题目描述思路代码C.TransportationExpenses题目描述思路代码D.AtCoderJanken3题目描述思路代码E.XorSigmaProblem题目描述思路代码A.Lea......
  • CF908E New Year and Entity Enumeration 题解
    CF908E给定\(m\),令\(M=2^m-1\)。给定\(\{0,1,\cdots,M\}\)的大小为\(n\)的子集\(T\),定义集合\(T\subseteqS\subseteq\{0,1,\cdots,M\}\)是好的当且仅当:\(a\inS\Rightarrowa\oplusM\inS\)\(a,b\inS\Rightarrowa\and\b\inS......
  • 题解:CF1209E1 Rotate Columns (easy version)
    题目传送门题意给出一个\(n\timesm\)的矩阵,我们可以对每一列进行循环位移,不限次数,最后求每一行的最大值之和。\(1\leqn\leq4,1\leqm\leq100\)思路注意到\(n\)的范围很小,那么我们也可以缩小\(m\)的范围。正确的方案显然是取整个矩阵的前\(n\)大值,并且将它......
  • CF1647F Madoka and Laziness 题解
    CF1647F给定排列\(p\),将其划分为两个单峰子序列,求两个单峰子序列的峰的组合的情况数。\(2\leqn\leq5\times10^5\)首先要注意到一个非常常见的地方:两个单峰子序列中的一个的峰值一定在整个排列\(p\)的最大值处这个非常显然,但并不注意到他的重要性,容易被忽视为......
  • LeetCode | 541 Reverse String II
    分析以2k作为游标步长,反转游标前半部分的字符串,后半部分保留;尾部部分余留长度,如果在[k,2k)直接处理情况跟前述一样,如果不是则直接返回。在这道题里面,还是回到数组部分提到的循环不变量法则,在2k长度这个游标移动过程中,处理完全一致:2k步长移动,只处理[2i,2i+k]部分,即便是尾部也是如......