当前位置: 代码迷 >> 综合 >> 【bzoj1965】【AHOI2005】洗牌
  详细解决方案

【bzoj1965】【AHOI2005】洗牌

热度:77   发布时间:2023-12-05 12:42:48.0

题意

??有一副牌,共n张( n 为偶数),每一次洗牌将其均分为上下两叠,然后取下叠第一张为新的第一张,上叠第一张为新的第二张,下叠第二张为新的第三张……求经过m次洗牌后,第 l 张牌的面值(初始为1n

解法

模拟:
??这道题比较容易,只要能够一步一步地分析下去:
??首先可以知道,对于现在处于第x张的牌来说,有:
①.若其在上叠( xn2 ),那么洗牌后它将会去到2?x的位置
??②.若其在下叠(x>n2),那么洗牌后它将会去到2?x?1的位置
??所以,最暴力的做法:开一个滚动数组,不停地向前模拟,得分20%
??如果将上面的式子变形,可以得到,对于现在处于第x张的牌来说,有:
①.若 x 为奇数,那么在洗牌前,这一张牌的位置是:n+x+12
??②.若x为偶数,那么在洗牌前,这一张牌的位置是: x2
??所以高级一点的暴力就是采用递归,不停的求解,由于爆栈,所以得分50%
??如果将递归改为递推,那么可以得到70%,最后的三个点会超时
??继续考虑优化:一个数x经过若干次变化之后,必定又会变回 x ,我们假设经过n+1次洗牌,那么x已经变化 n+1 次,而至多只有n个数,所以肯定又会变回去
于是我们首先模拟找到变化的循环节,然后用 m 对循环节取余,然后剩下的余数部分再次暴力模拟即可

复杂度

O(klogm),k为一不确定数字,大概在 11000

20分代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int MAXN=10000010;
int w[2][MAXN];
int n,m,l,cur;
int main()
{scanf("%d%d%d",&n,&m,&l);for(int i=1;i<=n;i++)   w[0][i]=i;while( m-- ){for(int i=1;i<=n/2;i++)   w[cur^1][2*i]=w[cur][i];for(int i=1;i<=n/2;i++)   w[cur^1][2*i-1]=w[cur][i+n/2];cur^=1;}printf("%d\n",w[cur][l]);return 0;
}

50分代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int MAXN=10000010;
int n,m,l,ans;
void work(int k,int pos)
{if( !k )   { ans=pos;return ; }if( pos%2 )   work( k-1,n/2+(pos+1)/2 );else   work( k-1,pos/2 );
}
int main()
{scanf("%d%d%d",&n,&m,&l);work( m,l );printf("%d\n",ans);return 0;
}

70分代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
using namespace std;
int n,m,l;
int main()
{scanf("%d%d%d",&n,&m,&l);for(int i=1;i<=m;i++){if( l%2 )   l=n/2+(l+1)/2;else   l=l/2;}printf("%d\n",l);return 0;
}

100分代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define Lint long long int
using namespace std;
Lint n,m,l,x,cnt=1;
int main()
{scanf("%d%d%d",&n,&m,&l);if( l%2 )   x=n/2+(l+1)/2;else   x=l/2;while( x!=l ){cnt++;if( x%2 )   x=n/2+(x+1)/2;else   x=x/2;}m=m%cnt;for(int i=1;i<=m;i++)if( l%2 )   l=n/2+(l+1)/2;else   l=l/2;printf("%d\n",l);return 0;
}