description:

一开始有两个数A和B,进行K轮游戏,每一轮可能有两种情况:

  • A B A \le B AB,本***作后 B = B A , A = A + A B' = B - A, A' = A + A B=BA,A=A+A.
  • B < A B < A B<A, 本***作后 A = A B , B = B + B . A'= A-B, B'=B+B. A=AB,B=B+B.

请输出 K K K轮游戏后 min ( A , B ) \min(A,B) min(A,B)的值。

A , B A,B \leq A,B K K \le K
20% 1 0 3 10^3 103 1 0 9 10^9 109
20% 1 0 6 10^6 106 1 0 9 10^9 109
20% 1 0 8 10^8 108 1 0 9 10^9 109
20% 1 0 9 10^9 109 1 0 7 10^7 107 *
20% 1 0 9 10^9 109 1 0 9 10^9 109

solution:

  • k 1 0 7 k\leq10^7 k107
    直接 O ( k ) O(k) O(k)暴力即可

  • A , B 1 0 3 a n d A , B 1 0 6 A,B\leq10^3 andA,B\leq10^6 A,B103andA,B106

A+B是一个循环节的长度,这样就可以通过直接除掉循环节来减少运算次数,但是A,B大了之后循环节就枚举不出来了。

  • A , B < = 1 0 8 A,B <= 10^8 A,B<=108

传统的找循环节⽅方法空间是 O ( A + B ) O(A+B) O(A+B)会超过内存限制

可以⽤用bitset把内存变成 O ( ( A + B ) / 32 ) O((A+B) / 32) O((A+B)/32)

b i t s e t < 200000000 > F ; bitset<200000000> F; bitset<200000000>F;

  • A , B < = 1 0 9 A,B <= 10^9 A,B<=109

S = A + B S = A+B S=A+B

A < = B A <= B A<=B(即S-A),则A’ = 2A = 2A mod S

A > B A > B A>B,则A’ = A - (S - A) = 2A - S = 2A mod S

所以答案为 A 2 K m o d S A * 2^K mod S A2KmodS

code:

刚开始的逐步骗分代码( 65 p t s 65pts 65pts):

//请输出k次后min(A,B)的值 
#include<cstdio>
using namespace std;
int min(int a,int b)
{
	if(a<=b)
	{
		return a;
	}
	else
	{
		return b;
	}
}
int x[10000005],y[10000005];
long long a,b,k;
void ANS()
{
	int flag=0;
	x[0]=a;
	y[0]=b;
	long long tmp=0,num=0;
	for(long long i=1;i<=k;i++)
	{
		if(x[i-1]<=y[i-1])
		{
			y[i]=y[i-1]-x[i-1];
			x[i]=x[i-1]*2;
		}
		else
		{
			x[i]=x[i-1]-y[i-1];
			y[i]=y[i-1]*2;
		}
		if(i==1)tmp=x[i];
		if(i!=1&&x[i]==tmp)
		{
			num=i;
			flag=1;
			break;
		}
	}
	if(flag==0)
	{
		printf("%d\n",min(x[k],y[k]));
		return;
	}
	num--;
	long long ans=k%num;
	printf("%d\n",min(x[ans],y[ans]));
}
int main()
{
	//freopen("game.in","r",stdin);
	//freopen("game.out","w",stdout); 
	scanf("%lld%lld%lld",&a,&b,&k);
	if(k<=10000000)//20%
	{
		for(long long i=1;i<=k;i++)
		{
			if(a<=b)
			{
				b=b-a;
				a=a+a;
			}
			else
			{
				a=a-b;
				b=b+b;
			}
		}
		printf("%d\n",min(a,b));
		return 0;
	}
	if(a<=1000&&b<=1000)//20%
	{
		ANS();
		return 0;
	}
	if(a<=1000000&&b<=1000000)//20%
	{
		ANS();
		return 0;
	}
	if(a<=100000000&&b<=100000000)//20% 
	{
		ANS();
		return 0;
	}
	else//听天由命 
	{
		ANS();
		return 0;
	}
} 
//955 812 828145516

/* 955253431 806956091 10000000 a=135061684 */

正解代码:

#include<cstdio>
using namespace std;
long long sqr(long long x)
{
	return x*x;
}
long long ksm(long long x,long long y,long long p)
{
	if(y==0)return 1;
	if(y%2==1)
	{
		return x*ksm(x,y-1,p)%p;
	}
	else
	{
		return sqr(ksm(x,y/2,p))%p;
	}
}
long long min(long long x,long long y)
{
	if(x<y)return x;
	else return y;
}
int main()
{
	//printf("%d\n",ksm(2,5,10));
	long long a,b,k;
	scanf("%lld%lld%lld",&a,&b,&k);
	long long ans=a*ksm(2,k,a+b)%(a+b);
	printf("%lld\n",min(ans,a+b-ans));
	return 0;
}