巧克力游戏
巧克力游戏
二维博弈论
出处: 25年浙江省省赛
-
题目大意: 有一块 n 行 m 列的巧克力,每格有一个正整数美味值 a_{i,j}。Putata 和 Budada 轮流吃,Putata 先手。每次操作可以选择吃掉剩余巧克力的最后 x 行(1 ≤ x ≤ 当前行数)或最后 y 列(1 ≤ y ≤ 当前列数),即只能从右下角切除一个连续的行块或列块。若某人操作后,剩余巧克力的总美味值 ≤ s,则此人被批评(输)。二人均采取最优策略避免被批评。
-
给定 q 个容忍度 s_i,对每个 s_i 判断最终谁会被批评。
-
输入格式
第一行一个整数 T,表示测试用例数。
每个测试用例:
第一行三个整数 n, m, q。
接下来 n 行,每行 m 个整数,表示 a_{i,j}。
接下来 q 行,每行一个整数 s_i。
对于每个查询,输出一行 "Putata"(如果 Putata 会被批评)或 "Budada"(如果 Budada 会被批评)。 -
数据范围
1 ≤ T ≤ 10^5
1 ≤ n·m ≤ 2×10^5
1 ≤ q ≤ 2×10^5
1 ≤ a_{i,j} ≤ 10^9
0 ≤ s_i < 所有 a_{i,j} 的总和
所有测试用例的 n·m 之和 ≤ 2×10^5 q 之和 ≤ 2×10^5定义坏格为(i, j)为顶点和(1, 1)围成的矩形中甜度<=s的格子,用L表示。P表示必败态,N表示必胜态。列数大于行数,否则就转置。
当一个格子上 左都是L的时候,这个格子为必败态,或者往上方和左方延伸,只能传递到必胜态的也是必败态。而某一个格子为必胜态,只有从该格子方和左方延伸,找得到必败态的时候,才能定义为必胜态。而由此可以得出,L往下方和右方延伸的格子都必然是必胜态。
而存在特殊情况,有些格子无法被覆盖:
L L L L L L L L L L L P
L L P N N N L L P N N N
L P N N N N L P N N N N
L N N P N N L N N P N N
L N N N P N L N N N P N
L N N N N P L N N N N N
L L P N N
L P N N N
L N N P N
L N N N P
L N N N N
右下方3*2的格子是由其他已知的部分推算的。(3, 4)因为无法转移到P,所以是P,然后该点下方和右方都转变为N,以此类推
填充必然使用如下矩阵,从左上角裁剪:
P N N N N
N P N N N
N N P N N
N N N P N
N N N N P
而观察可以发现,这些填充格子必然会补全原本没有P的行列,因为复杂区域就是由没有P的行列产生,而由P的分布可知,第一次出现的特殊区域必定在现在推导的矩形的右下角。
所以,只有当列数大于行数的时候,才能保证P不落在最后一格上面。当前面都满足,而最后一行满足为正方形时,P只能落在右下角。
定义f = i - e - (m - pi) 非满行-可用列。i为当前行,e是全为L的行数,pi是当前行的最大坏格列
必败条件:
前n行, f < 0 否则说明这一行的末尾存在P
第n行, f = 0
如果不存在复杂情况,那么(n-1, m-1)的区域必然全覆盖,而如果(1, m-1)为L,(1, m)不为,那么(1, m)为P。此时f=0
(n-1, 1)为L,而(n, 1)不为L,此时,非满行 = 可用列,说明下方是个正方形,而p不可能相邻,这就说明每个缝隙都被一个P填充
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
int n, m, q; cin >> n >> m >> q;
vector<vector<ll>> a(n, vector<ll>(m));
for(int i = 0; i < n; i ++)
for(int j = 0; j < m; j ++) cin >> a[i][j];
//转置
if(n > m) {
vector<vector<ll>> b(m, vector<ll>(n));
for(int i = 0; i < n; i ++)
for(int j = 0; j < m; j ++) b[j][i] = a[i][j];
swap(n, m);
a = move(b);
}
vector<vector<ll>> pre(n + 1, vector<ll>(m + 1, 0));
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++) pre[i][j] = pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1] + a[i-1][j-1];
vector<pair<ll, int>> queries(q);// 离线查询
for(int i = 0; i < q; i ++) {
cin >> queries[i].first;
queries[i].second = i;
}
sort(queries.begin(), queries.end());
vector<int> p(n + 1, 0);
vector<int> ans(q);
for(auto &[s, idx] : queries) {
for(int i = 1; i <= n; i ++) {
while(p[i] < m && pre[i][p[i] + 1] <= s) p[i] ++;
}
int e = 0;
while (e < n && p[e + 1] == m) e ++;
bool ok = 1;
for(int i = e + 1; i < n; i ++) {
if(i - e - (m - p[i]) >= 0) {
ok = 0;
break;
}
}
if(ok && n > e && n - e - (m - p[n]) != 0) ok = 0;
ans[idx] = ok;
}
for(const int& ok : ans) cout << (ok ? "Putata" : "Budada") << '\n';
}
int main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
int t; cin >> t;
while(t --) solve();
return 0;
}

浙公网安备 33010602011771号