首页 > 其他分享 >P9185 [USACO23OPEN] Rotate and Shift B 题解

P9185 [USACO23OPEN] Rotate and Shift B 题解

时间:2024-03-03 18:44:33浏览次数:17  
标签:USACO23OPEN P9185 int 题解 jmp cdot 数组 操作 order

首先,我们很容易就能得出一个显而易见的结论:

若令原数组为 \(order\),\(K\) 个活跃位置分别为 \(A_1,A_2,...,A_K\),则

\[order_{A_1} \to order_{A_2},order_{A_2} \to order_{A_3},...,order_{A_K} \to order_{A_1} \]

的操作就等价于将 \(order\) 数组顺时针旋转 \(x\) 次,即

\[order_1 \to order_2,order_2 \to order_3,...,order_N \to order_1 \]

再进行上述操作,最后逆时针旋转 \(x\) 次转回来。

因为顺时针和逆时针的旋转方向是相反的,都旋转 \(x\) 次正好抵消,所以上述结论的正确性也是显然的。

根据上述结论,题目所说的操作流程可以如下:

\[1. \ R \]

\[2. \ +1,R,-1 \]

\[3. \ +2,R,-2 \]

\[\cdot \cdot \cdot \]

\[T. \ +(T-1),R,-(T-1) \]

(其中 \(R\) 表示将 \(order\) 数组按 \(A\) 数组轮换的操作,\(+x\) 和 \(-x\)表示顺时针 / 逆时针旋转 \(x\) 次 \(order\) 数组的操作,\(x.\) 表示第 \(x\) 分钟进行的操作。)

这个过程中的顺时针和逆时针可以抵消,于是化简后的结果如下:

\[1. \ R,+1 \]

\[2. \ R,+1 \]

\[3. \ R,+1 \]

\[\cdot \cdot \cdot \]

\[T. \ R,+1,-T \]

这样化简之后,每分钟的操作都变成了 \(R,+1\),我们称这样的一次操作为 \(S\)。

于是整个题目的操作流程变成了每分钟进行一次 \(S\) 操作,最后逆时针旋转 \(T\) 次即可。

但是每次 \(S\) 操作的时间都是 \(O(N)\) 的,总时间复杂度还是 \(O(TN)\),和暴力并无区别。

那么如何加快 \(S\) 操作?既然朴素地旋转、轮换是不行的,那么我们就考虑使用倍增加快 \(S\) 操作的速度。

具体而言:

  • 建立倍增数组数组 \(jmp\),其中 \(jmp_{i,j}\) 表示用 \(2^j\) 次 \(S\) 操作能够把 \(i\) 挪到的位置。

  • 首先预处理 \(jmp\) 数组,令 \(jmp_{b_i,0}=i\),并令 \(jmp_{i,j}=jmp_{jmp_{i,j-1},j-1}\)(即从 \(i\) 用 \(2^{j-1}\) 次 \(S\) 操作挪到的位置开始再进行 \(2^{j-1}\) 次 \(S\) 操作。

  • 接着枚举 \(2\) 的 \(k\) 次幂,若 \(2^k \le T\) 则进行 \(2^k\) 次 \(S\) 操作。

最后别忘了逆时针再旋转 \(T\) 次就可以了。

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,k,t,tt; //tt是t的备份
int jmp[200031][64]; //倍增数组
int sor1[200031],sor2[200031],rot[200031],tmp[200031]; //order 数组,order 数组备份、A 数组、旋转辅助数组

void rotate1(){ //R 操作
	memset(tmp,-1,sizeof(tmp));
	for(int i=1;i<k;i++) tmp[rot[i]]=sor2[rot[i-1]];
	tmp[rot[0]]=sor2[rot[k-1]];
	for(int i=0;i<n;i++)
		if(tmp[i]+1) sor2[i]=tmp[i];
}
void rotate2(){ //顺时针旋转
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<n-1;i++) tmp[i]=sor2[i+1];
	tmp[n-1]=sor2[0];
	memcpy(sor2,tmp,sizeof(tmp));
}
void rotate3(){ //逆时针旋转
	memset(tmp,0,sizeof(tmp));
	for(int i=0;i<n;i++) tmp[i]=sor1[(i-tt%n+n)%n];
	memcpy(sor1,tmp,sizeof(tmp));
}

signed main(){
	cin>>n>>k>>t;
	for(int i=0;i<k;i++) cin>>rot[i];
	for(int i=0;i<n;i++) sor1[i]=sor2[i]=i;
	tt=t;
	
	rotate1(); //进行一次 R 操作 + 逆时针旋转一次,避免特判
	rotate2();
	
	for(int i=0;i<n;i++) jmp[sor2[i]][0]=i; //预处理 jmp 数组
	for(int j=1;j<=40;j++) //递推 jmp 数组
		for(int i=0;i<n;i++)
			jmp[i][j]=jmp[jmp[i][j-1]][j-1];
	for(int i=40;i>=0;i--){ //枚举 2^i
		if((1ll<<i)<=t){
			t-=(1ll<<i);
			for(int j=0;j<n;j++) tmp[jmp[j][i]]=sor1[j]; //进行 2^i 次 S 操作
			memcpy(sor1,tmp,sizeof(tmp));
		}
	}
	
	rotate3(); //逆时针旋转 T 次
	for(int i=0;i<n;i++) cout<<sor1[i]<<' ';
	return 0;
}

标签:USACO23OPEN,P9185,int,题解,jmp,cdot,数组,操作,order
From: https://www.cnblogs.com/XOF-0-0/p/18050464

相关文章

  • CF1833G Ksyusha and Chinchilla 题解
    首先,若\(n\bmod3\neq0\),则一定无解。考虑\(n\bmod3=0\)的情形:首先肯定是先进行一遍树形dp,求出树上每个节点\(x\)的子树大小\(size_x\)。若当前节点的\(size\)值\(=3\),则说明需要切断当前节点于其父节点的连边,使得其子树成为一个大小为\(3\)的单独连通块。......
  • ABC343 G Compress Strings 题解
    QuestionABC343GCompressStrings给定\(N\)个字符串\(S_1,S_2,\cdots,S_N\)找到一个包含所有这些字符串作为子字符串的最小长度的字符串一个字符串\(S\)包含一个字符串\(T\)作为子字符串是指:如果\(T\)可以通过从\(S\)的开头删除零个或多个字符以及从末尾删除......
  • AT_abc184_f [ABC184F] Programming Contest 题解
    题目传送门前置知识Meetinthemiddle解法非正解当成超大背包来做,暴力枚举每个数是否进行相加。时间复杂度为\(O(2^{n})\)。llp[50],ans=0;voiddfs(llx,lln,llm,llworth){ if(x==n+1) { if(worth<=m) { ans=max(ans,worth); } } else { if(wo......
  • P2532 [AHOI2012] 树屋阶梯 题解
    P2532[AHOI2012]树屋阶梯题解容易发现答案是卡特兰数,那么考虑证明这一点。考虑从左下角到右上角填满格子。利用动态规划的思想,回忆一下某道\(IOI\)的题目[数字三角形],每个格子的方案都只能由其左边或下边转移而来。可以结合图理解一下。好,刚才这个定义显然很符合卡特兰......
  • CF1931G One-Dimensional Puzzle 题解
    CF1931GOne-DimensionalPuzzle题解题意传送门思路考虑一下怎么入手,发现一个拼图只能接一些拼图(废话但是有用),所以我们可以简单地画出一个链接关系的图,\(u\tov\)表示编号为\(u\)的拼图后面能够接编号为\(v\)的拼图。然后我们发现问题转换为:......
  • AT_joig2021_d 展覧会 2 (Exhibition 2) 题解
    题目传送门前置知识二分答案解法最小值最大,考虑二分答案。关于check函数的书写,比luoguP1182数列分段SectionII多了个对位置的判定,注意对当前是第一次展出时进行特判。代码#include<bits/stdc++.h>usingnamespacestd;#definelllonglong#defineullunsigne......
  • 牛客练习赛122题解A-D
    牛客练习赛122A.黑白配代码:#include<bits/stdc++.h>usingnamespacestd;constintMAXN=1e5+7;constintINF=0x3f3f3f3f;typedeflonglongll;#defineendl"\n"voidsolve(){ intt,n;cin>>t>>n;inta[n+1];while(t--)......
  • CF1934题解
    题解首先拜谢波叔呀,一眼看上去没思路,直到看见了四重循环,大彻大悟。Solution没什么好说的,暴力四重题意大意就是给你一个数,在1,3,6,10,15中取数,使取出的数等于输入的数,求出至少需要用多少个数。求数代码: for(longlongi=0;i<=2;i++){ for(longlongj=0;j<=1;j++){ for(lo......
  • AT_abc169_f Knapsack for All Subsets题解
    如果我们定义\(dp[i][j]\)表示对于前i个字符而言,其子集满足条件的个数。那4么对于一个位置i而言,要么选择它贡献要么不选择,所以\(dp[i][j]=dp[i-1][j-a[i]](j>=a[i])+dp[i-1][j]\),这是每一个\(f(i)\)的函数。然后我们加上所有的\(dp[i][k](i:1到n)ans+=dp[i][k......
  • CF1383A String Transformation 1 题解
    若某一位\(i\)上\(A_i>B_i\),则显然无解。否则,建立并查集,然后遍历字符串,若\(A_i,B_i\)不在一个集合就合并\(A_i\)与\(B_i\),直到只剩下一个集合,此时的合并总次数即为答案。为什么呢?因为将\(A_i,B_i\)合并的操作可以视为等价于将以\(A_i\)开头的连续若干个相同字符均改......