题解:P7977 「Stoi2033」世界未末日
lailai0916 · · 题解
题意简述
有
判断先手是否必胜。
解题思路
先分析一堆石子的合法后继。
把
存在实根当且仅当判别式非负:
因为
其中:
当
记单堆游戏的 SG 函数为
从
而
不能逐个计算到
因此最小整数阈值满足:
代码用 sqrtl 得到近似平方根,再用整数乘法向两侧校正,保证上取整结果准确。阈值只有
由于大小为
对每个二进制位
当且仅当当前局面为必败态。
先证明从这种局面出发的任意操作都会离开它。取本次所有变化中的最高二进制位
再证明任意其他局面都能一步进入上述局面。按二进制位从高到低构造一次操作。已经在更高位变小的堆称为已选堆,它们的低位可以任意设置。设当前已有
令
若
若
故最终选择的堆数始终不超过
代码在读入每一堆时直接把各位计数对
总时间复杂度为
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=200005;
const int K=20;
ll d[N];
ll ceil_sqrt(ll x)
{
ll r=sqrtl(x);
while(r*r<x)r++;
while(r&&(r-1)*(r-1)>=x)r--;
return r;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,k;
ll mx;
cin>>n>>k>>mx;
int cnt=0;
while(d[cnt]<=mx)
{
d[cnt+1]=d[cnt]+ceil_sqrt(4*(d[cnt]+1))+2;
cnt++;
}
int base=k+1;
int bit[K]={};
for(int i=1;i<=n;i++)
{
ll s;
cin>>s;
int val=upper_bound(d,d+cnt+1,s)-d-1;
for(int j=0;val;j++,val>>=1)
{
if(!(val&1))continue;
bit[j]++;
if(bit[j]==base)bit[j]=0;
}
}
for(int i=0;i<K;i++)
{
if(!bit[i])continue;
cout<<"YES"<<'\n';
return 0;
}
cout<<"NO"<<'\n';
return 0;
}