码迷,mamicode.com
首页 > 其他好文 > 详细

约瑟夫-从模拟到毒瘤

时间:2019-10-05 18:32:00      阅读:59      评论:0      收藏:0      [点我收藏+]

标签:$1   题目   class   str   标签   clu   编号   cst   mod   

约瑟夫真是个好题。

约瑟夫的题有模拟,递推的标签。于是有两大类算法,三种题目。

入门练习类

复杂度$\Theta(NM)$

一般作者为了显示是个入门题会出$10^3 ~ 3 \times 10^4$的数据范围。(额,出到$10^5$仿佛还没见过)

而且一般会问整个序列。

模拟这个过程。

于是,有例子:

$5$个人报数,每报到$3$这个人就出列,下一个人从1开始报。

使用$LaTeX$。分$4$步

$$
\begin{array}{ccccc}
1&2&3&4&5\\
1&2&\not3&4&5\\
\not1&2&\not3&4&5\\
\not1&2&\not3&4&\not5\\
\not1&\not2&\not3&4&\not5
\end{array}
$$

于是最后只有$4$号留下了。

上个代码(模拟):

#include<cstdio>
int all[10000000];
int main(){
	int m,n;
	int bs=0,out=0,num=0;
//	out计数出去的人数	num记录当前的,	bs是报数计数器
    scanf("%d%d",&m,&n);
	do{
		++num;
		if(num==m+1) num=1; //循环下标
		if(all[num]==0) ++bs;//如果这个人没有出去,报数
		else continue; //已经出去了,跳过
		if(bs==n){//发现报到了
		    bs=0;
			all[num]=1;//干出去
			++out;//计数
			printf("%d ",num);//输出
		}
	}while(out!=m);//如果全出去了,结束。
	return 0;
}

递推类

时间复杂度$\Theta(N)$

于是$0<n,m<10^8$

并且只问最后一个人的编号。

于是递推。

如果我们要求$1$个人的序列的答案,

然后反向报数加人

可以先求出两个人的序列,在一直推到$N$个人,

每次重新编号,并且为了方便,我们把$[1,N]$平移到$[0,N-1]$。

于是本来有一个人。

后来的一个人排在了正确的位置上,

因为是反向报数,所以其实这个人就是最后出去的人。

而且现在的两个人中的最后出去的人的编号是对的。

于是一直推。

$$f_i=(f_{i-1}+m)\mod{i}$$

为了防止编号越出$n$,还得取个模。

放个代码(好短):

#include <iostream>
#include <cstdio>
#define LL long long

using namespace std;

LL ans=0,n,m;
int main(){
	ans=0;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
		ans=(ans+m)%i;
	printf("%lld\n",ans+1);
}

于是还有另类的题(不过也是逆推)

第$i$次报$i$个数

不过也一样,只是把里面的改成

for(int i=n-1;i>=0;i--)
	ans=(ans+i)%(n-i+1);

 一样是逆推。

毒瘤类:

当作者不再冷静,就会把数据出成这个鬼样子:

$N\leq 10^9,M \leq 10^6$

总之$\Theta(N)$过不去了。

那么我们就要想想为啥$M$这么小,

于是可以利用类似分块(?)的思想。

在$\Theta(N)$的基础上,把不需要取模的那部分直接乘法加速。

于是变成$\Theta(M \log N)$

就终于干过毒瘤出题人啦。

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

int m,n;
int main(){
	int T;
	for(cin>>T;T;T--){
		scanf("%d%d",&n,&m);
		int id=0;
		if(n>m){
			for(int i=1;i<=m;i++)
				id=(id+m)%i;
			for(int i=m+1;i<=n;i++){
				id=(id+m)%i;
				int jumpl=(i-id)/(m-1);
				if(i+jumpl<=n){
					id=(id+jumpl*m)%(i+jumpl);
					i+=jumpl;
				}
				else{
					id=id+(n-i)*m;
					break;
				}
			}
		}else{
			id=0;
			for(int i=1;i<=n;i++)
				id=(id+m)%i;
		}
		printf("%d\n",id+1);
	}
}

 

约瑟夫-从模拟到毒瘤

标签:$1   题目   class   str   标签   clu   编号   cst   mod   

原文地址:https://www.cnblogs.com/kalginamiemeng/p/11625108.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!