[HDU3544] Alice's Game(网上有些讲解是错的)

版权声明:本文为博主原创文章,可随意转载 https://blog.csdn.net/weixin_38686780/article/details/83039139

[HDU3544] Alice’s Game

题意:

 给n块巧克力,第i块是 n i m i n_i*m_i ,Alice只能垂直切,切成 A m B m A*m和B*m ,并且 A + B = n A+B=n ,Bob只能横切,只能切成 A n B n A*n和B*n ,并且 A + B = m A+B=m

分析:

语言表达能力有限

我们采取 H P ( n , m ) HP(n,m) 表示 n m n*m 的矩形对Alice的可切割次数的贡献,负数代表对Bob的贡献,如果所有 H P i > 0 \sum{HP_i}> 0 ,Alice必赢, H P i < = 0 \sum{HP_i} <= 0 ,Bob(必赢)
对于HP(i,j) 的计算我们有如下法则
1. n 1 n*1 的矩形贡献为n-1
2. 1 m 1*m 的矩形贡献为-(m-1)
3. 2 2 , 3 3 , 4 4.. n n 2*2,3*3,4*4..n*n 的矩形对HP的贡献为零,因为如果你首先下手切,都会给对手更多的机会,如果你能赢,你不会切这个,如果你输,那么切了这个你还会输。
4. 对于 2 3 , 3 2 , 5 4... , 2*3,3*2,5*4... , 的矩阵来说与3的状况相同,对答案的贡献都是0,首先下手都会给对手更多的机会
5. 贡献为零的有什么规律呢,我们发现原来是它们是 2 k < = n < 2 k + 1 2^k <= n < 2^{k+1} && 2 k < = m < 2 k + 1 2^k <= m < 2^{k+1}
6. n 2 n*2 的矩形对于Alice来说有贡献,我们每一次可以选择都切成 2 2 , 3 2 2*2,3*2 的矩形,这样不会给对手机会,自己还可以增加一次切的机会,Bob也不会傻到切这个矩形,这样会给Alice更多机会,所以 n 2 n*2 的矩形,Alice 可以切 n / 2 1 n/2-1
这个时候可以总结一下规律:

  1. 我们每一次切,都会切成 H P ( i , m ) = 0 , H P ( n i , m ) > = 0 HP(i,m) = 0,HP(n-i,m) >= 0 的 两块, H P ( n , m ) = H P ( n i , m ) + 1 HP(n,m) = HP(n-i,m)+1 ,递归下去 H P ( n i j , m ) = H P ( n i j , m ) + 1 HP(n-i-j,m) = HP(n-i-j,m)+1 ,直到 H P ( n i j . . . . , m ) = 0 HP(n-i-j....,m) = 0 ,这怎么计算呢
  2. 我们知道 H P ( n , m ) = 0 HP(n,m) = 0 当且仅当 2 k < = n < 2 k + 1 2^k <= n < 2^{k+1} && 2 k < = m < 2 k + 1 2^k <= m < 2^{k+1}
  3. 那么就有 H P ( n , m ) = n / ( 2 k ) 1 HP(n,m) = n/(2^{k}) - 1

在这里插入图片描述

参考代码

初级

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL solve(LL x,LL y)
{
    LL t  = 1;
    while(t*2 <= y)
        t <<= 1;
    return x/t;
}
int main()
{
    int T,n;
    scanf("%d",&T);
    for(int kase = 1; kase <= T; ++kase)
    {
        scanf("%d",&n);
        LL x,y;
        LL sum=0;
        for(int i=1; i<=n; i++)
        {
            scanf("%lld%lld",&x,&y);
            if(x > y) sum += solve(x,y)-1;
            else sum -= solve(y,x) -1;
        }
        printf("Case %d: ",kase);
        if(sum > 0) puts("Alice");
        else        puts("Bob");
    }
    return 0;
}

简化

下面这个版本就是大家在其他博客上看到的答案了
就是不停的对两边除以2,其实原理和 n / 2 k 1 n/2^{k}-1 相同

#include <cstdio>
typedef long long LL;
int main()
{

    int T; scanf("%d", &T);
    for(int kase = 1; kase <= T; kase++)
    {
        int n; scanf("%d", &n);
        LL a = 0, b = 0;
        while(n--)
        {
            int x, y;
            scanf("%d%d", &x, &y);
            while(x > 1 && y > 1) { x >>= 1; y >>= 1; }
            if(y == 1) a += (LL)x - 1;
            if(x == 1) b += (LL)y - 1;
        }
        printf("Case %d: %s\n", kase, a > b ? "Alice" : "Bob");
    }

    return 0;
}



猜你喜欢

转载自blog.csdn.net/weixin_38686780/article/details/83039139