[学习笔记]2018icpc沈阳K题-约瑟夫问题

这份题解大概是去年10月份打完组队训练写的,之前存在本地没发到博客,大概是昨天吃饭的时候和小伙伴聊到这题,和他口胡了一下大意,他叫我发给他(x),于是就有了这篇博客


K.Let the Flames Begin

题意

约瑟夫问题,\(n\)个人,报数到\(k\)出去,问第\(m\)个出去的人的初始位置,保证\(min\{m,k\}\leq 10^6\)

做法

呜呜呜场内没写出来

开始处理之前先把编号改一改,改成\(0,1,2,...,n-1\)比较好搞(

  • \(f(n,m)=[f(n-1,m-1)+k] \%n\)
  • \(f(n,m)\)表示\(n\)个人报数第\(m\)个出去的人的标号
  • 转移成\(n-1\)个人,对应的第\(m-1\)个出去的人,从他开始继续报到\(k\),对应的就是第\(m\)个出去的人的标号
  • \(n\)取模
  • 边界条件\(f(n,1)=(k-1)\%n\)

基于这一点能想到对\(m\)小的情形直接\(O(m)\)暴力

而对于\(k\leq m\)的情形呢?

  • \(k\)小,\(n\)大(\(m\leq n\)嘛)意味着间隔很小的距离就跳一次,需要取模的情况会比较少!
    即我们考虑从\(n-m+1\)往回推式子\(f(n,m)=[f(n-1,m-1)+k]\%n\) ,直观分析,需要跳很多步才会遇到一次需要取模的情况。

  • 而我们只要算出最多能连续跳并且不触发取模的步数\(t\),然后连着跳\(t+1\)步再对相应的\(n'\)进行取模,这个过程可以\(O(1)\)地实现

代码

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(register ll i=(a);i<=(b);i++)
typedef long long ll;
inline ll solve(ll n,ll m,ll k)
{
	if(k==1)return m-1;
	if(m<=k)
	{
		ll res=(k-1)%(n-m+1);
		rep(i,n-m+2,n)res=(res+k)%i;
		return res;
	}
	ll len=n-m+1;
	ll res=(k-1)%(n-m+1);
	m--;
	
	while(m>0)
	{
		ll t=(len-res)/(k-1);
		if(m<=t)
		{
			res=(res+m*k)%(len+m);
			m=0;
		}else
		{
			res=(res+t*k)%(len+t);
			res=(res+k)%(len+t+1);
			m-=t+1;
			len+=t+1;
		}
	}
	return res;
}

int main()
{
	int T;scanf("%d",&T);
	ll n,m,k;
	rep(t,1,T)
	{
		scanf("%lld%lld%lld",&n,&m,&k);
		printf("Case #%lld: %lld\n",t,solve(n,m,k)+1);
	}
	return 0;
}

核心代码是

ll len=n-m+1;
ll res=(k-1)%(n-m+1);
m--;
while(m>0){
    ll t=(len-res)/(k-1);
    if(m<=t){
        res=(res+m*k)%(len+m);
        m=0;
    }else{
        res=(res+t*k)%(len+t);
        res=(res+k)%(len+t+1);
        m-=t+1;
        len+=t+1;
    }
}

这一段。

\(len\)表示当前的\(n'\),res记录答案,用递推式从小到大倒推回去

每次算出的最多能跳的步数\(t\)注意和\(m\)进行比较

*复杂度分析

(这一部分证明思路来自@Mobius Meow大神x)

  • 考虑记\(y\)为当前的\(n'\),记\(x\)为当前进行迭代的次数。
  • 对于\(y\)我们可以以\(y=tk\)进行估算,同时注意到\(\begin{aligned}\frac{dy}{dx}=\frac{tk-k}{k}=t-1\end{aligned}\) 这样一个近似的关系
  • 得到方程\(\begin{aligned}\frac{dy}{dx}-\frac{y}{k}=-1\end{aligned}\)
  • 这个方程很好解,最后再把\(y\)换成题目里的n,得到\(x=kln(n-k)-Ck\)
  • 所以整个算法时间复杂度为优秀的\(O(klog(n))\)
posted @ 2021-02-28 06:28  yoshinow2001  阅读(212)  评论(0编辑  收藏  举报