51nod 1661 黑板上的游戏(yjq魔改ver【x【博弈,稍微数学推一下

20 篇文章 0 订阅
2 篇文章 0 订阅

题意差不多就是那样……但是输出的方案(题面原话)是→“我们需要使得擦去的数字下标尽量大,在此前提下, 我们希望写上的数字尽量大”

总之我胡搞乱搞只有52分,嗨呀好气啊,std是yjq的代码……yjq说自己看不懂当时写的啥了……于是…………唉说多了都是泪【x

嗯……我手推SG值玩了一张纸,写完代码发现……woc看错题了,有理数除法看成了下取整,GG

又玩了一张纸的SG值【我是智障吗】然后和电脑对拍【我果然是智障】

……其实手推比较容易找规律,讲真

总之就看出了这个鬼畜的规律

1.当x%k==1时,他的SG值是从前面递归搞过来的,并没有更新【反正手跑一下就显然了

2.其他时候,SG值是x-x/k,除法上取整


于是开始考虑如何得到那个【最大的下标】

1.SG值不是单调的,所以并不能单纯地用sg(a[i])>sum_xor,显然sg[a[i]]==sum_xor也是可以卡的【虽然yjq表示自己就是这么写的 …………

2.只有x%k==1的时候才会出现一个不单调的点(下陷),其他时候SG值都是x-x/k,而且会发现,当x%k==1时,用通式算得的值和SG(x-1)应该是一样的【证明什么的……应该是显然吧,x'比x大1,但上取整的除法结果也大了1】,所以我们可以把SG值序列当成单调不降的,并且直接用通式算出可以转移到的最大sg值(max_sg)

3.只要max_sg(a[i])>=sum_xor^a[i],就可以通过修改当前位置的值来得到最优答案,总之从右往左for一遍,判一下就好


然后是那个最大的解

1.可以直接for,T不T看脸

2.可以二分,然而基本写挂了,有个大爷写的l到r的区间小于等于1e5直接暴力for,看起来挺靠谱但是复杂度有毒

3.yjq表示你们啊naive,作为一个长者我写的是O(1)的【x

4.对于一个式子g=x-ceil(x/k),我们设x=ak+b+1 (b∈[0,k-1]),那么,ceil(x)=a+1,于是g=ak+b+1-a-1=a(k-1)+b

g=a(k-1)+b;

1) 如果g%(k-1)==0 那么b也等于0,说明x=ak+1=g/(k-1)*k+1,但是这样得到的解显然不是合法的(因为在x%k==1时,这是一个凹下去的点),但之前我们证明了在此时 SG(x-1)==SG(x),所以可以得到一个解(x-1)

2) 在其他情况下,可以求得b=g%(k-1),所以x=ak+1+b=g/(k-1)*k + 1 + g%(k-1)

5.当前我们得到了一个解,但不一定是最优的,于是根据当时手跑SG值得到的结论,对于一个数a,SG(a)==SG(ak+1),于是在小于当前修改位置上的值的情况下,稍微递推一下就好,因为一直在*k,所以最坏复杂度也是O(log值域)的

我觉得我一个下午就推了这一道题真是太菜了,但是总觉得可以hack一下std了迷之开心【有毒吧你】

最后感谢出题人yjq考试T1暴力分竟然可以用轻度优化水80,重度优化直接A


#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;	int n;long long k;
long long a[MAXN],sg[MAXN];

long long SG(long long x){
	while(x>k&&!((x-1)%k))	x=(x-1)/k;
	return x-1-(x-1)/k;
}
long long max_SG(long long x){
	return x-1-(x-1)/k;
}

long long now_xor=0;

long long find_x(long long sgg,long long lmt){
	long long tmp=sgg/(k-1)*k+1;
	if(sgg==0)	tmp=1;
	else
		if(sgg%(k-1))	tmp+=sgg%(k-1);
		else	--tmp;
	while((lmt-1)/tmp>k)	tmp=tmp*k+1;
	return tmp; 
}

int main(){
	scanf("%d%lld",&n,&k);
	for(int i=1;i<=n;++i)	scanf("%lld",a+i),sg[i]=SG(a[i]),now_xor^=sg[i];
	if(!now_xor)	return puts("Bob"),0;
	else	printf("Alice ");
	for(int i=n;i;--i){
		if(max_SG(a[i])>=(now_xor^sg[i])){
			long long tmp=now_xor^sg[i];
			tmp=find_x(tmp,a[i]);
			printf("%d %lld",i,tmp);
			break;
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值