1 条题解

  • 0
    @ 2025-8-24 22:24:17

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar qczrz6v4nhp6u
    Tell me, what scares you.

    搬运于2025-08-24 22:24:17,当前版本为作者最后更新于2024-12-10 10:49:09,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    好玩的题目。

    Solution

    考虑判定一个局面是先手必胜还是后手必胜。一眼丁真每个棋子都是独立的,所以直接启动 SG{\rm SG}

    然后发现每个状态的后继都是一个链状的结构,这跟 Nim 游戏是一样的,所以我们知道 SG(i)=log2ni{\rm SG}(i)=\lfloor\log_2\frac ni\rfloor。于是一个局面的 SG{\rm SG} 值 ${\rm SG}(1\sim n)=\bigoplus_{1\le i\le n}\lfloor\log_2 \frac ni\rfloor$。

    枚举每一个值并统计它出现了多少次,推式子:

    $${\rm SG}(1\sim n)=\bigoplus_{k}\Big(\big(\lfloor\frac{n}{2^k}\rfloor-\lfloor\frac n{2^{k+1}}\rfloor\big)\bmod 2\Big)\times k $$

    其中 $\big(\lfloor\frac{n}{2^k}\rfloor-\lfloor\frac n{2^{k+1}}\rfloor\big)$ 就是满足 ni[2k,2k+1)\frac{n}{i}\in [2^k,2^{k+1})ii 的个数。观察一下,发现 $\Big(\big(\lfloor\frac{n}{2^k}\rfloor-\lfloor\frac n{2^{k+1}}\rfloor\big)\bmod 2\Big)$ 就是 nn 的第 kk 位与第 k+1k+1 位是否相同。

    接下来考虑拆位,即统计 nn 的每一位贡献到 SG{\rm SG} 值的系数。打表 code:

    #include<bits/stdc++.h>
    using namespace std;
    using ui=unsigned int;
    using ll=long long;
    using ull=unsigned long long;
    using i128=__int128;
    using u128=__uint128_t;
    using pii=pair<int,int>;
    #define fi first
    #define se second
    ull f[6];
    void init(){
    	for(int i=0;i<64;i++)
    		for(int j=0;j<6;j++)
    			if(i>>j&1)
    				f[j]^=3ull<<i;
    }
    int n;
    int main(){
    	ios::sync_with_stdio(false);
    	cin.tie(0),cout.tie(0);
    	init();
    	for(int i=0;i<6;i++)
    		for(int j=0;j<64;j++)
    			cerr<<(f[i]>>j&1)<<" \n"[j==63];
    	return 0;
    }
    

    打出来的表长这样:

    0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    0 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
    0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0
    0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    

    不难发现规律:第 00 列的系数均为 00;设 j=log2lowbit(i)j=\log_2{\rm lowbit}(i),则 [0,j][0,j] 行内第 ii 列的贡献系数都是 11(j,+)(j,+\infty) 行内第 ii 列的贡献系数都是 00

    对它消元,使得每列仅出现一个 11。那么此时的后手必胜要求就是对于每一种 jj,所有满足 lowbit(i)=j{\rm lowbit}(i)=j 的数位 ii 异或起来是 00

    现在我们有相当好的办法刻画 SG{\rm SG} 值,直接从高到低确定每一位即可。时间复杂度 O(logn)O(\log n)

    Code

    #include<bits/stdc++.h>
    using namespace std;
    using ui=unsigned int;
    using ll=long long;
    using ull=unsigned long long;
    using i128=__int128;
    using u128=__uint128_t;
    using pii=pair<int,int>;
    #define fi first
    #define se second
    ui n;bool val[64];
    int main(){
    	ios::sync_with_stdio(false);
    	cin.tie(0),cout.tie(0);
    	cin>>n;n++;
    	ull ans=0,cnt=1ull<<29;
    	for(int i=35;i>=1;i--){
    		if(!(i&(i-1)))ans|=(ull)val[i]<<i;
    		else{
    			if(n>cnt)
    				n-=cnt,ans|=1ull<<i,val[i&-i]^=1;
    			cnt>>=1;
    		}
    	}
    	cout<<(ans+(n>1))<<'\n';
    	return 0;
    }
    
    • 1

    信息

    ID
    5884
    时间
    1000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者