P2148 [SDOI2009]E&D
题意:
有2n堆石子,第2k-1堆和第2k堆是一组,现在两个人轮流操作,每次操作任选一组石子,然后将改组中的一堆石子移走,将另一堆式子分割成两堆,形成新的两堆石子,要求每堆石子数必须大于0,谁先不能操作谁输掉游戏
题解:
我一开始先打表,利用sg的性质打表,但是没有发现啥规律,然后看了题解,题解里都是打表,不过人均一眼看出规律。。还是我太菜了
std::map<pii, int> sg;int calc(pii c) {if (sg.count(c)) return sg[c];std::vector<int> s;for(int i=1;i<=c.first-1;i++) s.push_back(calc({i, c.first - i}));for(int i=1;i<=c.second-1;i++) s.push_back(calc({i, c.second - i}));std::sort(s.begin(), s.end());s.erase(std::unique(s.begin(), s.end()), s.end());int lst = -1;for (auto i : s) {if (i != lst + 1) return sg[c] = lst + 1;lst = i;}return sg[c] = lst + 1;
}
int main()
{//rd_test();pii a={50,50};calc(a);for(int i=1;i<=50;i++){for(int j=1;j<=50;j++){printf("i=%d j=%d sg=%d\n",i,j,sg[{i,j}]);}}//Time_test();
}
有人将打表结果制图得到:
选自博客
然后根据规律得到sg函数:
#define c(x, p) (x % p ? x % p : p) // 0 % p = p
int sg(ui x, ui y) {for (ui i = 0, p = 2; i < 31; i++, p *= 2) {if ((c(x, p) <= p / 2) && (c(y, p) <= p / 2)) return i;}return 31;
}
额我是没发现。。后来一艘发现这个图的样子有学名:阿达马矩阵
对于某一个矩阵H[t]递归定义为
H[1] = [1]
H[t] = [0] H[t-1]
H[t-1] H[t-1]
还有一个结论:
f(x):表示x的二进制末尾首个0的出现位置(下标从0开始),比如:f(5)=f(101)2f(101)_{2}f(101)2=1
sg(x,y)为一组分别有x+1,y+1个石子的sg值
SzS_{z}Sz表示满足x+y+1=z的sg(x,y)构成的自然数几集合
根据sg性质可知:
sg(x,y)=mex(SxS_{x}SxUSyS_{y}Sy)
结论:
SzS_{z}Sz等同于 z 二进制下 1 的位置集合。例如 S5S_{5}S5 = {0,2}
sg(x,y)=f(x|y),例如sg(1,4)=f(5)=1
证明:
具体证明
代码:
// Problem: P2148 [SDOI2009]E&D
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2148
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Data:2021-08-13 20:35:05
// By Jozky#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
template <typename T> inline void read(T& x)
{T f= 1;x= 0;char ch= getchar();while (0 == isdigit(ch)) {if (ch == '-')f= -1;ch= getchar();}while (0 != isdigit(ch))x= (x << 1) + (x << 3) + ch - '0', ch= getchar();x*= f;
}
template <typename T> inline void write(T x)
{if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef ONLINE_JUDGE
#elsestartTime= clock();freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef ONLINE_JUDGE
#elseendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
#define c(x, p) (x % p ? x % p : p) // 0 % p = p
int sg(int x, int y)
{for (int i= 0, p= 2; i < 31; i++, p*= 2) {if ((c(x, p) <= p / 2) && (c(y, p) <= p / 2))return i;}return 31;
}
int main()
{//rd_test();int t;cin >> t;while (t--) {ll sum= 0;int n;cin >> n;for (int i= 1; i <= n; i+= 2) {int x, y;cin >> x >> y;sum^= sg(x, y);}if (sum)puts("YES");elseputs("NO");}return 0;//Time_test();
}