「BJOI2018」双人猜数游戏(博弈+dp)

https://loj.ac/problem/2511

不知道很多轮后就知道了是因为可能的数对在不断减少。

考虑设\(f[z][x][y]\)表示\(z\)轮后,两个数分别为\(x,y\)是否能猜出来。

\(z=1\)时,\(f=可能的数对(u,v)只有一个\)

\(z>1\)
转移1:\(f[z][x][y]=f[z-2][x][y]\)
转移2:枚举所有可能的数对\((u,v)\),若\((u,v)(除(x,y))\)\(z-1\)轮都被猜出来,则\(f[z][x][y]=1\)

最后,答案要的是两个人都知道的时间是\(t+1\)\(t+2\)

\((x,y)\)合法的条件:
\((x,y)\)最早知道时间是\(t+1\),且对于所有可能的数对\((u,v)\),只有\((x,y)\)\(t+1\)知道,那么\(t+2\)时另一人就可以知道。

利用记忆话搜索+map可以\(1s\)内出所有解,

Code

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

string in, out, num;

void init(int t) {
	num = "";
	for(; t; t /= 10) num = ((char) (t % 10 + '0')) + num;
	in = "guess" + num + ".in";
	out = "guess" + num + ".out";
}

int s, t; char str[105];

const int N = 100005;

map<int, int> bz[18][N], f[18][N];

int dg(int z, int x, int y) {
	if(bz[z][x][y]) return f[z][x][y];
	bz[z][x][y] = 1;
	if(z == 1) {
		if(str[0] == 'A') {
			int cnt = 0;
			fo(i, s, x * y) if(x * y % i == 0 && i <= x * y / i)
				cnt ++;
			f[z][x][y] = cnt == 1;
		} else {
			int cnt = 0;
			fo(i, s, x + y) if(i <= x + y - i)
				cnt ++;
			f[z][x][y] = cnt == 1;
		}
		return f[z][x][y];
	}
	f[z][x][y] = z > 2 ? dg(z - 2, x, y) : 0;
	if(!f[z][x][y]) {
		if(((str[0] == 'A') + z) % 2 == 0) {
			int cnt = 0;
			fo(i, s, x * y) if(x * y % i == 0 && i <= x * y / i) {
				cnt += !dg(z - 1, i, x * y / i);
			}
			if(cnt == 1 && !dg(z - 1, x, y))
				f[z][x][y] = 1;
		} else {
			int cnt = 0;
			fo(i, s, x + y) if(i <= x + y - i) {
				cnt += !dg(z - 1, i, x + y - i);
			}
			if(cnt == 1 && !dg(z - 1, x, y))
				f[z][x][y] = 1;
		}
	}
	return f[z][x][y];
}

int n;

int qry(int x, int y) {
	fo(i, 1, t + 1) if(dg(i, x, y))
		return i;
	return -1;
}

void work() {
	fo(i, 1, 17) fo(j, 1, 1e5) bz[i][j].clear();
	scanf("%d", &s);
		scanf("%s", str);
		scanf("%d", &t);
		fo(sum, 2 * s, 100000) {
			fo(x, s, sum / 2) {
				int y = sum - x;
				if(qry(x, y) == t + 1) {
					int cnt = 0;
					if(((str[0] == 'A') + t) % 2 == 0) {
						int cnt = 0;
						fo(i, s, x * y) if(x * y % i == 0 && i <= x * y / i) {
							cnt += (qry(i, x * y / i) == (t + 1));
						}
						if(cnt == 1) {
							pp("%d %d\n", x, y);
							return;
						}
					} else {
						int cnt = 0;
						fo(i, s, x + y) if(i <= x + y - i) {
							cnt += (qry(i, x + y - i) == (t + 1));
						}
						if(cnt == 1) {
							pp("%d %d\n", x, y);
							return;
						}
						
					}
				}
			}
		}
}

int main() {
	fo(T, 1, 25) {
		fprintf(stderr, "%d\n", T);
		init(T);
		freopen(in.c_str(), "r", stdin);
		freopen(out.c_str(), "w", stdout);
		work();
	}
	
}
posted @ 2020-04-14 11:31  Cold_Chair  阅读(477)  评论(0编辑  收藏  举报