Gym - 102911C B - Chocolate Game: Love is War (对称博弈 + 预处理质因子)

链接
B - Chocolate Game: Love is War
题意:
有两堆石子,一堆石子的数目范围是[ m − x , m ] ,另外一堆石子数目范围是[ n − y , n ]。
在确定了两堆石子的初始数目后,两个人轮流操作。每个人选一堆石子取,要求剩下的石子是是原来石子数的因数。先无法操作的人失败。求多少种初始状态使得先手必胜。
思路:

  1. 考虑对称性,如果两堆石子初始数量相同,那么先手必败,因为无论先手怎么取石子,后手总可以在另外一堆石子中进行相同的操作。再普遍一点,我们可以发现 ,如果质因子数相同,那么两堆石子就是等价的,例如 4 和 9 是等价的 ,8 和 12 也是等价的,因为他们唯一分解后 质因子的幂次和相同。也会满足上述的对称原理。所以我们只要预处理出 每个数的质因子幂次和 就可以了。
  2. 关于预处理每个数的质因子,有两种方法,一种是先预处理每个数的最小质因子,然后要处理的数一直除最小质因子,比普通的分解方法要快得多,这种方法是 nlogn 的。还有一种线性的方法,也比普通的方法要快。
    处理方法 一
int isp[maxn];  //存最小质因子
int pri[maxn];
int cnt[maxn];
void init(){
     for(int i = 2; i <= N; i ++){
         if(!isp[i]){
            pri[tot++] = i,isp[i] = i;
         }
         for(int j = 0; i * pri[j] <= N ; j ++){
            isp[i * pri[j]] = pri[j];
            if(i % pri[j] == 0) break;
         }
     }
     for(int i = 2; i <= N; i ++){
        int temp = 0,x = i;
        while(isp[x]){
            x /= isp[x];
            temp++;
        }
        cnt[i] = temp;
    }
}

处理方法二

void init(){
     for(int i = 2; i <= N; i ++){
         if(!isp[i]){
            pri[tot++] = i,isp[i] = i;
         }
         for(int j = 0; i * pri[j] <= N ; j ++){
            isp[i * pri[j]] = pri[j];
            if(i % pri[j] == 0) break;
         }
     }
}
void init1(){
    init();
    cnt[1] = 0;
    for(int i = 1; i < N; i ++){
        for(int j = 0;i * pri[j] < N && j < tot; j ++){
            cnt[i * pri[j]] = cnt[i] + 1;
        }
    }
}

完整代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e6 + 7;
const int mod = 1e9 + 7;
const int N = 5e6 + 7;
int T;
int n,m,x,y,tot;
int isp[maxn];
int pri[maxn];
int cnt1[maxn],cnt2[maxn],cnt[maxn];
void init(){
     for(int i = 2; i <= N; i ++){
         if(!isp[i]){
            pri[tot++] = i,isp[i] = i;
         }
         for(int j = 0; i * pri[j] <= N ; j ++){
            isp[i * pri[j]] = pri[j];
            if(i % pri[j] == 0) break;
         }
     }
}
void init1(){
    init();
    cnt[1] = 0;
    for(int i = 1; i < N; i ++){
        for(int j = 0;i * pri[j] < N && j < tot; j ++){
            cnt[i * pri[j]] = cnt[i] + 1;
        }
    }
}
int main() {
    init1();
    /*for(int i = 2; i <= N; i ++){
        int temp = 0,x = i;
        while(isp[x]){
            x /= isp[x];
            temp++;
        }
        cnt[i] = temp;
    }*/
    scanf("%d%d%d%d",&m,&n,&x,&y);
    ll ans = 1ll * (x + 1) * (y + 1);
    for(int i = m - x; i <= m; i ++){
        cnt1[cnt[i]]++;
    }
    for(int i = n - y; i <= n; i ++){
        cnt2[cnt[i]]++;
    }
    for(int i = 0; i <= 40; i ++){
        ans -= 1ll * cnt1[i] * cnt2[i];
    }
    printf("%lld\n",ans);


    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值