Luogu

LOJ

分析

下面的 $n,m$ 和题面上是反的(因为我写完才发现这个问题所以就懒得改了)。

先复原一下样例一($s=5,nm=60,n+m=16$)的游戏过程中双方的心理活动。

  • Bob:$(n,m)$ 的可能情况有 $(5,11),(6,10),(7,9),(8,8)$,所以回答不知道。
  • Alice:$(n,m)$ 的可能情况有 $(5,12),(6,10)$,两种情况 Bob 之前的回答都应该是不知道,无法排除任何一种,所以回答不知道。
  • Bob:$(n,m)$ 的可能情况有 $(5,11),(6,10),(7,9),(8,8)$,但是对于 $(5,11),(7,9),(8,8)$ Alice 的可能情况只有一种,所以 Alice 应该回答知道,但是 Alice 回答的是不知道,所以 $n=6,m=10$,回答知道。
  • Alice:$(n,m)$ 的可能情况有 $(5,12),(6,10)$,但是对于 $(5,12)$ Bob 应该回答不知道(因为对于 $n+m=17$ Bob 还有 $(7,10),(8,9)$ 无法排除掉),所以 $n=6,m=10$,回答知道。
  • Alice和 Bob分别单独写出了正确的 $n$ 和 $m$ ,观众们觉得 Alice 和 Bob很厉害。

那么可以考虑 DP。设 $dp_{i,j,k}$ 表示回答了 $i$ 次不知道时能否确定 $n=j,m=k$。

考虑转移。首先会有 $dp_{i,j,k}\leftarrow dp_{i-2,j,k}$。其次,假设当前是 Alice(Bob 同理),那么枚举 $j\times k$ 的所有约数 $x$,如果只有一个 $dp_{i-1,x,j\times k/x}=0$ 且这个 $x=j$,那么可以确定 $j,k$。

考虑求答案。枚举 $n,m$,那么一对 $n,m$ 满足条件至少要有 $dp_{t,n,m}=1$ 且 $\forall k<t,dp_{k,n,m}=0$。其次还要保证另一个人在当前这个人回答知道后能够确定 $n,m$,做一次类似的转移即可。

DP 的上界可以自己调一调,事实上都不到 $500$,一个点在本地跑也不要几秒。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int L=500;

int s,t; char name[10];
int dp[20][L+10][L+10];

bool solveAlice(int t,int x,int y) {
    int l=x*y,ansc=0,ansx,ansy;
    for (int i=s;i*i<=l;++i) {
        if (l%i) continue;
        if (!t||!dp[t-1][i][l/i])
            ++ansc,ansx=i,ansy=l/i;
    }
    return ansc==1&&ansx==x&&ansy==y;
}
bool solveBob(int t,int x,int y) {
    int l=x+y,ansc=0,ansx,ansy;
    for (int i=s;i+i<=l;++i)
        if (!t||!dp[t-1][i][l-i])
            ++ansc,ansx=i,ansy=l-i;
    return ansc==1&&ansx==x&&ansy==y;
}
bool checkAlice(int t,int x,int y) {
    int l=x*y,ansc=0,ansx,ansy;
    for (int i=s;i*i<=l;++i) {
        if (l%i) continue;
        if (dp[t][i][l/i]&&(t<2||!dp[t-2][i][l/i]))
            ++ansc,ansx=i,ansy=l/i;
    }
    return ansc==1&&ansx==x&&ansy==y;
}
bool checkBob(int t,int x,int y) {
    int l=x+y,ansc=0,ansx,ansy;
    for (int i=s;i+i<=l;++i)
        if (dp[t][i][l-i]&&(t<2||!dp[t-2][i][l-i]))
            ++ansc,ansx=i,ansy=l-i;
    return ansc==1&&ansx==x&&ansy==y;
}

int main() {
    file(guess);
    s=read(); scanf("%s",name); t=read();
    for (int i=0,o=(name[0]=='A');i<=t;++i,o=!o)
        for (int j=1;j<=L;++j)
            for (int k=1;k<=L;++k) {
                if (i>=2) dp[i][j][k]=dp[i-2][j][k];
                if (o) dp[i][j][k]=solveAlice(i,j,k);
                else dp[i][j][k]=solveBob(i,j,k);
            }
    for (int l=s+s;;++l)
        for (int i=s;i+i<=l;++i) {
            int x=i,y=l-i;
            if (!dp[t][x][y]) continue;
            int flag=0;
            for (int j=0;j<t;++j)
                if (dp[j][x][y]) { flag=1; break; }
            if (flag) continue;
            if (t&1) {
                if (name[0]=='A') flag=checkAlice(t,x,y);
                else flag=checkBob(t,x,y);
            } else {
                if (name[0]=='A') flag=checkBob(t,x,y);
                else flag=checkAlice(t,x,y);
            }
            if (flag) { printf("%d %d\n",x,y); return 0; }
        }
    return 0;
}
最后修改:2020 年 10 月 24 日 04 : 42 PM