poj 2369 Buttons
http://poj.org/problem?id=2368
【巴什博奕经典问题】一堆n个物品,两个人A、B轮流取物品,规定每人每次至少拿一个,最多拿m个,最后取光者得胜。
巴什博奕求解
n = (m + 1) × r + s
①若 n <= m , 第一个先拿的赢;
②若 n > m ,如果 n % (m + 1) == s > 0,第一个人拿s,之后保证留给第二个人(m + 1)的倍数则最终一定获胜,反之, n % (m +1)==0,第二个人赢。
若仅仅判断孰输孰赢,直接判断n%(m+1)==0?Second : First.
思路
此题的背景是巴什博奕,但要求的是最小的m,解决方法是先判断n是否为素数,是的话m = n -1,否则找n大于2的最小约数即可,投机取巧O(n)找的约数QAQ
参考代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef long long ll;
const int N = 10; //换底的次数,次数越多,n为素数的概率越大
ll n; //n=10^14,普通测素数的方法不到70组数据就会超时,何况n可能会更大(Java?)
ll random(ll n)
{
return (ll)((double)rand()/RAND_MAX*n+0.5);
}
ll multi(ll a,ll b,ll mod)
{
ll ans=0;
while(b){
if(b&1) ans=(ans+a)%mod;
b>>=1;
a=(a+a)%mod;
}
return ans;
}
ll quickpow_mod(ll a,ll b,ll mod) //long long 10^19 数据很大时乘法a*a也会溢出,也应二分写乘法取模
{
ll ans=1;
while(b){
if(b&1) ans=multi(ans,a,mod);
b>>=1;
a=multi(a,a,mod);
}
return ans;
}
bool Miller_rabin(ll n)
{
for(int i=0;i < N; i++){
ll a =random(n-2) + 1;
if(quickpow_mod(a,n-1,n)!=1) return false;
}
return true;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("input.txt","r",stdin);
#endif // ONLINE_JUDGE
while(scanf("%lld",&n) == 1){
if(Miller_rabin(n)) printf("%lld\n",n-1);//素数的最小m为n-1
else{
ll m = 0;
for(ll i = 3; i <= n; ++ i)
if(n%i==0) {m = i - 1;break;}
printf("%lld\n",m);
}
}
return 0;
}
- 加粗
Ctrl + B
- 斜体
Ctrl + I
- 引用
Ctrl + Q
- 插入链接
Ctrl + L
- 插入代码
Ctrl + K
- 插入图片
Ctrl + G
- 提升标题
Ctrl + H
- 有序列表
Ctrl + O
- 无序列表
Ctrl + U
- 横线
Ctrl + R
- 撤销
Ctrl + Z
- 重做
Ctrl + Y