获胜条件不一样的博弈
一.题目链接
https://codeforces.com/gym/102821/problem/G
二.题意
T组数据,给出x,y,k,w.w=0 Alice先手; w=1 Bob先手.每一次可以x-1或者y-1.
获胜条件:
- x=k或者y=k,bob赢.
- 否则,如果,x和y都是质数,alice赢.
数据范围 : 2<=x,y<=1e6 2<=k<=min(x,y)
三.算法分析
特殊情况是: 一开始就定胜负了.
首先获胜条件太复杂,考虑转化.
考虑二维平面坐标系,bob赢的条件是走到直线x=k或者直线y=k上.而alice想要在走的过程中走到一个双质数点.
那么就可以转化为"在走到走到直线x=k或者直线y=k前,alice想要走到特殊点,而bob想要避开特殊点".
先考虑bob先手,发现alice必胜的条件为(x,y)=(质数+t,质数+t)(注意这个质数得>k),因为bob不管选哪个,alice就选另一个,总可以走到特殊点.而如果(x,y)不满足这样的条件,则bob必胜,因为bob总有办法可以让alice避开特殊点.
再考虑alice先手,其后继态为bob先手(x-1,y)和bob先手(x,y-1),然后就可以套用上面一段了.
四.代码
#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define clr(a, b) memset(a, b, sizeof(a))
#define rep(i,a,b) for (register int i=(a); i<=(b); i++)
#define per(i,a,b) for (register int i=(a); i>=(b); i--)
#define debug(x) std::cerr << #x << '=' << x << std::endl
using namespace std;
typedef long double ld;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef vector<int> Vi;
template<class T> inline void read(T &x) {
x=0;
char c=getchar();
int f=1;
while (!isdigit(c)) {
if (c=='-') f=-1;
c=getchar();
}
while (isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
x*=f;
}
template<class T> T gcd(T a, T b) {
return !b?a:gcd(b,a%b);
}
template<class T> inline void umin(T &x, T y) {
x=x<y?x:y;
}
template<class T> inline void umax(T &x, T y) {
x=x>y?x:y;
}
const double EPS = 1e-8;
const double PI = acos(-1.0);
const int maxn=1e6+2000;
bool vis[maxn],isprime[maxn];
int pri[maxn],cnt,cs1[maxn],cs2[maxn];
void init() {
for (int i=2; i<=1e6; i++) {
if (!vis[i]) isprime[i]=1,pri[cnt++]=i;
for (int j=0; j<cnt; j++) {
if (1ll*i*pri[j]>1e6) break;
vis[i*pri[j]]=1;
}
}
}
//bob先手,x,y在到达k的过程中能否遇到质数(alice能否赢得比赛)
bool check(int x,int y,int k) {
if (x==k || y==k) return 0;
if (isprime[x] && isprime[y]) return 1;
if (x<y) swap(x,y);
set <int> s1;
s1.clear();
for (int i=0;pri[i]<x;i++) {
if (pri[i]<=k) continue;
s1.insert(x-pri[i]);
}
// bool f1=0;
for (int i=0;pri[i]<y;i++) {
if (pri[i]<=k) continue;
if (s1.count(y-pri[i])) return 1;
}
return 0;
}
int main() {
init();
int T;
cin>>T;
int x,y,k,w;
for (int it=1; it<=T; it++) {
scanf("%d%d%d%d",&x,&y,&k,&w);
if (x==k || y==k) {
printf("Case %d: Bob\n",it);
continue;
} else {
if (isprime[x] && isprime[y]) {
printf("Case %d: Alice\n",it);
continue;
}
}
if (w) {
if (check(x,y,k)) printf("Case %d: Alice\n",it);
else printf("Case %d: Bob\n",it);
} else {
if (check(x-1,y,k) || check(x,y-1,k)) printf("Case %d: Alice\n",it);
else printf("Case %d: Bob\n",it);
}
}
return 0;
}
五.总结
- 对于赢法不一样的博弈,先去找到一种某人必胜的玩法,然后用他拓展.
- 把check()单独写出来,否则容易漏掉
if (x==k || y==k) return 0; if (isprime[x] && isprime[y]) return 1;
这种东西.