CodeChef A Game With a Sheet of Paper

传送门

题意

一张 \(n × m\) 的网格,其中某个格子 \((x, y)\) 被涂黑了。两个玩家轮流
操作,每次沿一条水平或竖直的格线将网格切成两部分,并舍弃不含黑
格子的一部分。无法操作者败。
双方均以最优策略行动。问有多少种 \((x, y)\) 的选择使得先手必胜。
\(n, m ≤ 1e6\)\(100\) 组测试数据。

题解

考虑当下方红色格子被全部切出去时,整个网格也就全部被切出去了。否则游戏必然还没有结束。
image
那么题目可以转化成,有4组格子,每次可以选中任意一组删掉若干个,典型的NIM游戏。
我们考虑求不合法方案数,然后用\(n × m\)减去不合法即为答案。
转化为求 \((x, y)\) 使得 \(x \oplus (n-1-x) \oplus y \oplus (m-1-y) = 0\),复杂度爆炸。
考虑实际上 我们是要求有多少 \((x, y)\), 使得\(x \oplus (n-1-x) = y \oplus (m-1-y)\)
考虑左右柿子的值都是\(O(n)\)的,分别统计每种取值有多少种情况即可。
摆烂,详见代码。

实现

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;

int read(){
    int num=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) num=num*10+c-'0', c=getchar();
    return num;
}

const int N = 1e6+100;
int T, n, m;
int r[N], c[N];

int main(){
    T = read();
    while(T--){
        n=read(), m=read();
        ll res = 0;
        for(int i=0; i<N; i++) r[i]=c[i]=0;
        for(int i=0; i<n; i++) r[i^(n-i-1)]++;
        for(int i=0; i<m; i++) c[i^(m-i-1)]++;
        for(int i=0; i<N; i++) res += 1ll*r[i]*c[i];
        printf("%lld\n", 1ll*n*m-res);
    }
    return 0;
}
posted @ 2022-03-02 15:25  ltdJcoder  阅读(20)  评论(0编辑  收藏  举报