NOIP模拟69
T1:
Nim游戏变形
首先先补博弈论:传统Nim游戏是:
对于n堆石子,每堆石子有a[i]个,每次任取(但不能不取)石子,无法石子者负
那么必胜策略为a[i]异或和不为0,结论比较显然
首先考虑异或的意义,根据异或的运算法则,显然可以对于每一位由处在该位上的1的奇偶进行判断
那么异或和为0的意义也就是若将所有石子二进制分解,那么对于每一位上都存在偶数堆
考虑首先当所有堆都为0时显然必负
而当异或和为0时,根据上述异或意义的简述,显然不存在一种取法使得异或和仍为0
因为二进制意义下并不存在某数在某一二进制位上的数为非0偶数(只有0、1),也就是当异或和为0时,操作者无法打破局面,
接下来考虑当异或和不为0时,操作者始终存在一种方法使得异或和变为0
简单的构造,根据异或运算消去律,直接取a[i] ^ x即可
综上所述,当异或和为0时先手必负,因为后手始终可以构造出异或和为0的面,最终所有数为0时先手无法再取,反之先手必胜
对于本题而言,并不是完全的Nim游戏,原因在于本题存在取石子数量的限制
而上述策略的建立条件之一为任取石子,然而显然可以转化为Nim游戏,其实可以倒推,假设当前取石子上界为x,两人分别为a,b
考虑首先只剩x个石子时显然a必胜,考虑如何构造这种局面,显然当b取石子时还剩下x + 1个石子,同理分析,我们可以构造得出
若将每x + 1个石子分为一组(x + 1为能够分出胜负的最小石子数),那么当a[i] % (x + 1) == 0时先手必负,反之先手必胜
上述为1堆石子的策略,考虑合并,显然对于当前这一局游戏而言x是一定的,而我们已经通过分析得出当x + 1 | a[i]时
先手必负,于是我们考虑对于每一堆石子都对(x + 1)取模,那么我们实际上只需要对于余数分析即可,因为剩余所有局面都为
先手必负,并不会对当前局面造成影响,那么显然由于当前所有a[i]都为x + 1的余数,问题就转化为Nim游戏,直接异或判断即可
考虑对于这类问题更为一般的解法:SG函数
首先需要知道mex运算与SG函数的定义,简单应用,接下来只说其正确性与原理:
考虑将上述问题的考虑方式(即x + 1)推广,我们发现,一个分出胜负的局面即为操作者所能构造局面的mex
因为无论a如何操作都无法超出mex,反之b则可以将局面补回,即转化为另一mex局面
因此得出SG函数的定义:SG(x)为其所有后继状态y[i]的SG函数值构成的集合取mex的结果
(整个有向图的SG函数值定义为根节点s的SG函数值)
其实与上述问题非常相似,对于有向图游戏的和的SG函数值定义为其异或和,分析同理
另:对于博弈论可以通过构造有向图,通过必胜必负的相互关系,推导出所有状态的胜负情况,时间复杂度O(n + m)
回到本题,于是问题转化为求对于所有a[i] % (x + 1)的异或和,考虑优化,由于x不断变化,于是考虑更为一般的优化方式
首先比较显然的思路为倍数发枚举降低时间复杂度,于是问题转化为对于若干区间求区间内数 % (x + 1)异或和,发现可以转化为
区间内数-区间左端点,于是考虑维护出f[i][j]表示从i往后所有x - i的结果,位运算显然按位考虑,于是将dp转化为x - i包含
j这一二进制位,考虑如何转移
对于并不明显的状态转移方程可以考虑状态划分,由于二进制下对于高位操作显然不会对低位产生影响,于是划分出2^(j + 1)
得到f[i][j] = f[i + 2^(j + 1)][j] + sigma (i + 2^j,i + 2^(j + 1) - 1)c[k],前缀和优化c[k]即可
考虑如何合并出一段区间的结果,首先后缀相减显然是错的,因为dp状态的第一维并不相同,于是考虑将上述转移方程拓展
同理,发现若将一段区间按2^j长度进行划分,那么从起始点开始,只有编号为偶数的区间会造成贡献,十分显然,那么直观想法为
对于要求的区间l,r计算出区间长度能够划分为几段2^j,后缀相减再通过前缀和加回来,然而并不正确,考虑后缀相减的前提:
同样是上述转移方程的拓展,只有当转移状态二进制下不存在低于j的位时才可转移
即f[i][j]必须由f[i + k][j]转移而来,k二进制下0~j位全部为0
考虑如何解决这一限制,可以将区间长度由2^j转化为2^(j + 1),于是直接后缀相减,再判断多减的区间是否存在贡献(是否需要补加贡献,
因为只有偶数段会造成贡献)即可
代码如下:
1 #include <bits/stdc++.h>
2 using namespace std;
3 #define I int
4 #define C char
5 #define B bool
6 #define V void
7 #define D double
8 #define LL long long
9 #define UI unsigned int
10 #define UL unsigned long long
11 #define P pair<I,I>
12 #define MP make_pair
13 #define a first
14 #define b second
15 #define lowbit(x) (x & -x)
16 #define debug cout << "It's Ok Here !" << endl;
17 #define FP(x) freopen (#x,"r",stdin)
18 #define FC(x) freopen (#x,"w",stdout)
19 const I N = 5e5 + 3;
20 I n,buc[N],f[N][20],t[20],p[N],Pre[N];
21 inline I read () {
22 I x(0),y(1); C z(getchar());
23 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
24 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar();
25 return x * y;
26 }
27 inline V Max (I &a,I b) { a = a > b ? a : b; }
28 inline V Min (I &a,I b) { a = a < b ? a : b; }
29 inline I max (I a,I b) { return a > b ? a : b; }
30 inline I min (I a,I b) { return a < b ? a : b; }
31 inline V swap (I &a,I &b) { a ^= b, b ^= a, a ^= b; }
32 inline I abs (I a) { return a >= 0 ? a : -a; }
33 inline P operator + (const P &a,const P &b) {
34 return MP (a.a + b.a,a.b + b.b);
35 }
36 inline P operator - (const P &a,const P &b) {
37 return MP (a.a - b.a,a.b - b.b);
38 }
39 signed main () {
40 n = read ();
41 t[0] = 1;
42 for (I i(1);i < 20; ++ i)
43 t[i] = t[i - 1] << 1;
44 for (I i(1);i <= n; ++ i)
45 buc[read ()] ++ , p[i] = log2 (i);
46 p[n + 1] = log2 (n + 1);
47 for (I i(1);i <= n; ++ i)
48 Pre[i] = Pre[i - 1] + buc[i];
49 for (I i(n); ~i ; -- i)
50 for (I j(0);j <= p[n - i + 1]; ++ j)
51 f[i][j] = f[min (i + t[j + 1],n)][j] + Pre[min (i + t[j + 1] - 1,n)] - Pre[i + t[j] - 1];
52 for (I i(2);i <= n + 1; ++ i) {
53 I tmp (0); B flag (0);
54 for (I k(0);k <= p[i - 1]; ++ k) {
55 for (I j(0);i * j <= n; ++ j) {
56 I l (i * j), r (min (i * (j + 1) - 1,n)), typ (r - l + 1 >> (k + 1));
57 if (f[l][k] - f[l + typ * t[k + 1]][k] + (l + typ * t[k + 1] + t[k] <= r) * (Pre[r] - Pre[l + typ * t[k + 1] + t[k] - 1]) & 1)
58 tmp ^= 1 << k;
59 }
60 if (tmp) { flag = 1; break; }
61 }
62 flag ? printf ("Alice ") : printf ("Bob ");
63 }
64 }
T3:
枚举GCD,判断合法情况即可
代码如下:
1 #include <bits/stdc++.h>
2 using namespace std;
3 #define I long long
4 #define C char
5 #define B bool
6 #define V void
7 #define D double
8 #define LL long long
9 #define UI unsigned int
10 #define UL unsigned long long
11 #define P pair<I,I>
12 #define MP make_pair
13 #define a first
14 #define b second
15 #define lowbit(x) (x & -x)
16 #define debug cout << "It's Ok Here !" << endl;
17 #define FP(x) freopen (#x,"r",stdin)
18 #define FC(x) freopen (#x,"w",stdout)
19 const I mod = 1e9 + 7;
20 inline I read () {
21 I x(0),y(1); C z(getchar());
22 while (!isdigit(z)) { if (z == '-') y = -1; z = getchar(); }
23 while ( isdigit(z)) x = x * 10 + (z ^ 48), z = getchar();
24 return x * y;
25 }
26 inline V Max (I &a,I b) { a = a > b ? a : b; }
27 inline V Min (I &a,I b) { a = a < b ? a : b; }
28 inline I max (I a,I b) { return a > b ? a : b; }
29 inline I min (I a,I b) { return a < b ? a : b; }
30 inline V swap (I &a,I &b) { a ^= b, b ^= a, a ^= b; }
31 inline I abs (I a) { return a >= 0 ? a : -a; }
32 inline P operator + (const P &a,const P &b) {
33 return MP (a.a + b.a,a.b + b.b);
34 }
35 inline P operator - (const P &a,const P &b) {
36 return MP (a.a - b.a,a.b - b.b);
37 }
38 I GCD (I a,I b) {
39 return b == 0 ? a : GCD (b,a % b);
40 }
41 signed main () {
42 FP (hacker.in), FC (hacker.out);
43 I a (read ()), b (read ()), c (read ()), d (read ()), ans (0);
44 for (I i(1);i < 1000; ++ i)
45 for (I j(1);i + j < 1000; ++ j)
46 if (GCD (i,j) == 1) {
47 I r1 (b / i), l1 (ceil(1.0 * a / i));
48 I r2 (d / j), l2 (ceil(1.0 * c / j));
49 if (min (r1,r2) >= max (l1,l2))
50 (ans += (i + j) * (min (r1,r2) - max (l1,l2) + 1) % mod) %= mod;
51 }
52 printf ("%lld\n",ans);
53 }