#include #include #include #include #include #include using namespace std; typedef long long ll; const int N = 2e5 + 10; const ll INF = 1e18; int t, n, m, a[N]; ll f[N], s[N], g[N], pos[N]; vector v; ll calc(int l, int r) { // 计算区间异或和 if (l > r) return 0; return s[r] ^ s[l - 1]; } int getpos(int x) { // 获取当前数字在序列中的位置 return lower_bound(pos + 1, pos + m + 1, x) - pos; } void solve() { scanf("%d%d", &n, &m); v.clear(); for (int i = 1; i <= m; ++i) { scanf("%d", &a[i]); v.push_back(a[i]); } sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); m = v.size(); for (int i = 1; i <= m; ++i) pos[i] = v[i - 1]; for (int i = 1; i <= m; ++i) s[i] = s[i - 1] ^ (pos[i] - pos[i - 1] - 1); // 用异或和存储位置之间的距离 for (int i = 1; i <= m; ++i) g[i] = calc(1, i - 1) ^ calc(i + 1, m); // 计算左右两边异或和 f[0] = 0; for (int i = 1; i <= m; ++i) { int p = getpos(a[i]); if (p & 1) { // 如果当前位置所在区间中有奇数个1,则当前状态为必败态,可以直接赋值为INF f[i] = INF; continue; } f[i] = max(g[i], 0ll); // 初始状态 for (int j = 1; j <= i; ++j) { int k = i - j; if (getpos(a[j]) > p || getpos(a[j]) & 1) break; // 如果j的位置在当前位置的右侧或者所在区间有奇数个1,则退出循环 if (getpos(a[j]) == p - 1) { // 如果j刚好在当前位置所在区间的左侧,则直接取反 f[i] = max(f[i], g[i] ^ (s[p - 1] ^ s[j - 1]) ^ 1); } else { // 否则,先取反到区间左端点,再从左端点跳到j,然后再跳到i f[i] = max(f[i], g[i] ^ (s[p - 1] ^ s[j - 1]) ^ (f[j - 1] ^ calc(j + 1, p - 2) ^ calc(p, k))); } } } if (f[m] == 0) puts("TIN"); else puts("NIT"); } int main() { scanf("%d", &t); while (t--) solve(); return 0; }

题目描述nn给出长度为-n-的-01序列a_1~n序列中有偶数个-1。NIT-和-TIN-轮流做以下操作NIT-先手:nn--选择位置-$i-1le-ile-n$满足区间-$1i$-中有奇数个-1。再选择位置-$j-ijle-n$。将-$a_ia_j$-都取反即0-变-11-变-0nn当整个序列中的所有元素都变为-0-时当前轮到的人就无法操作他就输了。假设-NIT-和-TIN-都绝顶聪明谁会赢?可以证明游戏总会结束。nn$n$-可能很大但序列中-$1$-的个数不超过-$2times-10^5$。

原文地址: https://www.cveoy.top/t/topic/rAz 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录