蓝桥云课 OJ Alice和Bob的爱恨情仇
雾粉和数论
Alice和Bob的爱恨情仇
问题描述
Bob 和 Alice 最近在学习博弈论,为了学以致用,他们找来了一大堆的小饼干,并通过博弈的方式来吃掉这些小饼干。他们将找来的小饼干分成 𝑛n 堆,每堆小饼干有 𝑎𝑖a**i 个小饼干。他们轮流对这些饼干进行操作,操作规则如下:
-
由 Alice 先手,每次从一堆小饼干中拿出 𝑘𝑚k**m 个小饼干( 𝑘k 为奇数且 𝑚≥0m≥0,且 𝑘𝑚k**m 不能超出该堆小饼干的总数)。
-
当一方进行完操作后,如果已经没有剩余的小饼干,则该方获胜,赢得所有的小饼干。
Alice 和 Bob 都想赢得所有的小饼干,所以都会以最佳方法来取小饼干,请问他们之中谁能赢得所有的小饼干?
输入格式
第一行,输入两个正整数 𝑛n (1≤𝑛≤2×106)(1≤n≤2×106) , 𝑘k (1≤𝑘≤109)(1≤k≤109) ,分别表示饼干的堆数和每次取出饼干的底数。
第二行,输入 𝑛n 个整数,表示第 𝑖i 堆小饼干有 𝑎𝑖a**i(1≤𝑎𝑖≤1061≤a**i≤106) 个小饼干。
输出格式
输出一行,包含一个字符串,输出 Alice 和 Bob 之中获胜的那个人。
样例输入 1
2 3 4 1
样例输出 1
Alice
样例输入 2
2 1 6 6
样例输出 2
Bob
样例说明
在第一个样例中,Alice 拿走第一堆饼干中的 33 (𝑘1)(k1) 个饼干,此时剩余的饼干数分别为 1,1 ,此时,Bob 无论拿哪堆饼干都会使得剩下那堆饼干只有一个,Alice 只需要在下一轮拿走剩余的饼干即可获胜。
在第二个样例中,由于每次都只能取一个,且有 1212 个饼干,所以先手的人到最后一定会输掉比赛。
思路展示
这道题看似起来很难,一开始接触的时候觉得离谱,该题的m竟然没有告诉我们,我还打算求出来,通过转进制的方式设置while循环,后面看到大佬的一个if判断条件瞬间悟了,虽然这道题挂的是博弈论,实际上直接写暴力就好。首先要判断两个值,一个是n堆饼干的总数以及m,求二者的奇偶性,因为题目设置了先让Alice拿饼干,所以当分四种情况,(奇,奇),(偶,偶),(奇,偶),(偶,奇),讲一种情况,依次类推,当n为奇数,m为奇数的时候,最后一次一定是Alice,通过样例就可以得知,就是因为Alice是先手。代码如下(有点繁琐,轻喷)
// 三年不鸣则已,易鸣惊人 // 五年不飞则已,一飞冲天 #include<bits/stdc++.h> using namespace std; using ll=int64_t; using ULL=unsigned long long; #define INF 0x3f3f3f3f #define llINF 0x3f3f3f3f3f3f3f3f const int R=4e5+10; typedef pair<int,int>PII; ll sum=0; const int N=1e5+10; int a[N]; void solve(){ int n,m;cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; sum+=a[i]; } if(sum%2!=0&&m%2!=0){ printf("Alice"); } if(sum%2!=0&&m%2==0){ printf("Bob"); } if(sum%2==0&&m%2==0){ printf("Bob"); } if(sum%2==0&&m%2!=0){ printf("Bob"); } } int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int T;T=1; while(T--){ solve(); } return 0;
如果这道题没有考虑到奇偶性,时间复杂度相当高,所以写这篇博客的目的是再三提醒自己不要忘了奇偶性,我立马想到了另外一道题目--雾粉和数论。
雾粉和数论
题目描述
给你一个正整数 n,请你输出对每个2<=i<=n,gcd(i*(i-1)/2,i(i+1)/2)之和对10^9+7取模。
输入描述:
第一行有一个正整数,表示n。 题目保证对于所有测试用例: 2<=n<=10^9
输出描述:
输出有一行一个正整数,表示答案模 10^9+7 后的值。
示例一
输入
复制
3
输出
4
说明
gcd(1,3)+gcd(3,6)=1+3=4
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网
先来给大家展示WA了n发的代码。
// 三年不鸣则已,易鸣惊人 // 五年不飞则已,一飞冲天 #include<bits/stdc++.h> using namespace std; using ll = int64_t; using ULL = unsigned long long; #define INF 0x3f3f3f3f #define llINF 0x3f3f3f3f3f3f3f3f const int R = 4e5 + 10; typedef pair<int, int>PII; inline int gcd(int a, int b) { int r; while (b > 0) { r = a % b; a = b; b = r; } return a; } ll sum = 0; void solve() { int n; cin >> n; for (int i = 2; i <= n; i++) { sum += gcd(i * (i - 1) / 2, i * (i + 1) / 2); } cout << sum << endl; } int main() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int T;T=1; while (T--) { solve(); } return 0; }
纯暴力直接超时,赛后经高人指点,发现直接通过奇偶性解决。分两种情况,当i为奇数的时候,(i-1)/2和(i+1)是互质,所以最大公约数就是i。当i为偶数的时候,最大公约数为i/2。所以直接化简到到最后直接数列求和(i是从2开始的),同理,还需要对n的奇偶性进行判断,当n为奇数时,偶数项是从1开始到(n-1)/2的求和((n-1)/2项),奇数项是从3开始到n的求和(n/2项),n为偶数亦是如此。以下为AC代码。
// 三年不鸣则已,易鸣惊人 // 五年不飞则已,一飞冲天 #include<bits/stdc++.h> using namespace std; using ll=int64_t; using ULL=unsigned long long; #define INF 0x3f3f3f3f #define llINF 0x3f3f3f3f3f3f3f3f const int R=4e5+10; typedef pair<int,int>PII; const int N=1e9+7; void solve(){ ll sum=0,yiming=0; ll n;cin>>n; if(n%2!=0){ sum=((3*n*n+4*n-7)/8)%N; cout<<sum<<endl; } if(n%2==0){ ll t=n/2; yiming=(ll) ( ( (ll)n*(n/2)/2-1) %N + (ll)t*(t+1)/2%N +N)%N; cout<<yiming<<endl; } } int main() { ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); int T;T=1; while(T--){ solve(); } return 0; }