Arpa and a game with Mojtaba 有
n
n
n 个数字,第
i
i
i 个为
a
i
a_i
ai 双方轮流操作。每次选择一个质数
p
p
p 和一个正整数
k
k
k,要求至少要有一个数字
a
i
a_i
ai 满足
p
k
∣
a
i
p^k|a_i
pk∣ai 然后,把所有满足上述要求的
a
i
a_i
ai 变为
a
i
p
k
\frac{a_i}{p^k}
pkai 不能操作的人输。问先手胜负情况。
1
≤
n
≤
100
1\le n\le 100
1≤n≤100
1
≤
a
i
≤
1
0
9
1\le a_i\le 10^9
1≤ai≤109
思路
首先可以想到,对于不同质数,每个数字可以写成这个质数的
x
x
x 次方。对于不同质数来说是独立的游戏,所以我们只用考虑对于某个质数
p
p
p,有一个集合
S
S
S,存储着有些数字是
p
p
p 的多少次方。
但是这个转移非常不好做。我们假设使用状压做。 记集合为
S
S
S,每次选择一个
k
k
k,就是要把这其中所有
>
k
>k
>k 的元素都
−
k
-k
−k,在二进制表示就相当于把
S
S
S 变为了
(
S
>
>
k
+
1
)
∣
(
S
&
(
(
1
<
<
k
)
−
1
)
)
(S>>k+1) | (S\&((1<<k)-1))
(S>>k+1)∣(S&((1<<k)−1)) 然后求出所有后续状态的
S
G
SG
SG 值,就能获得当前状态的
S
G
SG
SG 值。由于
S
S
S 的范围可以达到
2
e
9
2e9
2e9,我们用
m
a
p
map
map 去记忆化存储。
代码
时间复杂度:感觉可能是
O
(
2
e
9
×
30
×
o
(
m
a
p
)
)
O(2e9\times 30\times o(map))
O(2e9×30×o(map)),但是其实是
O
(
飞
快
)
O(飞快)
O(飞快)
Tokitsukaze and Duel | CF1190C 给定一个长度为
n
n
n 的
01
01
01 串
S
S
S,以及一个
k
k
k 两个人轮流操作。每次选择一个长度为
k
k
k 的子串,把他们都变成
1
1
1 或者
0
0
0。 某人操作之后,串全部为
0
0
0 或者全部为
1
1
1 就赢了。 求先手胜负情况,或者平局。
题目给了一个关键样例
n
=
4
,
k
=
2
,
S
=
{
0101
}
n=4,k=2,S=\{0101\}
n=4,k=2,S={0101} 注意到,先手不管怎么操作,都能搞出三个连续的数字,然后后手可以轻松获胜。 所以我们想到,如果先手不管怎么操作,都能怎么怎么样,那么就是输,否则就是平局。
首先想一想,这个怎么怎么样是什么。 显然,如果不管怎么操作,子串两侧的串如果都是一边全是
1
1
1,一边全是
0
0
0,且
2
k
≥
n
2k\ge n
2k≥n,那么明显先手必败。 如果
2
k
<
n
2k<n
2k<n,且上述条件达到了的话,后手是无法在一步之内获胜的。 如果不能在一步之内获胜,那么一定是平局,因为后手可以重复先手的操作,让串不发生变化。
代码
时间复杂度:
O
(
n
)
O(n)
O(n)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}typedeflonglong ll;constint MAX =1e5+50;constint MOD =1e9+7;char ss[MAX];int pre[MAX];intone(int l,int r){if(l > r)return0;return pre[r]- pre[l-1];}intzero(int l,int r){if(l > r)return0;return r - l +1-one(l,r);}intmain(){int n,k;scanf("%d%d%*c",&n,&k);scanf("%s",ss +1);for(int i =1;i <= n;++i){
pre[i]= pre[i-1]+(ss[i]=='1');}
bool canwin = false;
bool alwaysfail = true;
bool both = false;for(int i =1;i + k -1<= n;++i){int Lone =one(1,i-1);int Lzero=zero(1,i-1);int Rone =one(i+k,n);int Rzero=zero(i+k,n);if(Lone + Rone == n - k){// 先手赢的两种情况
canwin = true;}if(Lzero + Rzero == n - k){
canwin = true;}if(Lone && Rone)both = true;// 如果操作后两边都有 1 或者 0,那么就不会输,至少是平局的if(Lzero && Rzero)both = true;}if(canwin){puts("tokitsukaze");}elseif(k *2>= n &&!both){puts("quailty");}else{puts("once again");}return0;}
H:Warrior and Archer | CF594A
题意
Warrior and Archer 有
2
n
2n
2n 个在坐标轴上的位置
x
i
x_i
xi。
A
l
i
c
e
Alice
Alice 和
B
o
b
Bob
Bob 轮流删除这上面的位置,直到只有两个位置,这俩位置之间的距离为
S
S
S
A
l
i
c
e
Alice
Alice 希望最小化
S
S
S,
B
o
b
Bob
Bob 希望最大化
S
S
S。你需要求出最后的
S
S
S
2
≤
2
n
≤
2
×
1
0
5
2\le 2n\le 2\times 10^5
2≤2n≤2×105
0
≤
x
i
≤
1
0
9
0\le x_i\le 10^9
0≤xi≤109
思路
容易想到,
A
l
i
c
e
Alice
Alice 只会删除所有位置中的两侧的位置。她删除中间的位置只会是更劣的。 其次,更容易想到
B
o
b
Bob
Bob 只会删除最终中间的位置。也就是说,如果最后剩下的两个位置为
i
,
j
i,j
i,j,那么
B
o
b
Bob
Bob 一定是删除
[
i
+
1
,
j
−
1
]
[i+1,j-1]
[i+1,j−1] 的所有位置,而
A
l
i
c
e
Alice
Alice 删除的就是
[
1
,
i
−
1
]
,
[
j
+
1
,
2
n
]
[1,i-1],[j+1,2n]
[1,i−1],[j+1,2n] 的位置。
经过这样的思考,我们直接枚举
A
l
i
c
e
Alice
Alice 最左边删除了几个点,最右边删除了几个点,那么答案就是这中间的所有点的最两侧的位置的距离。取这个距离的最小值即可。
代码
时间复杂度:
O
(
n
)
O(n)
O(n)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}typedeflonglong ll;constint MAX =2e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;int aa[MAX];intmain(){int n;scanf("%d",&n);for(int i =1;i <= n;++i)scanf("%d",&aa[i]);sort(aa+1,aa+1+n);int ans = INF;for(int i =0,ed =(n -2)/2;i <= ed;++i){int zuo =1+ i;int you = n -(ed - i);
ans =min(ans,aa[you]- aa[zuo]);}printf("%d",ans);return0;}
F:Cookies | CF1099F
题意
Cookies 给定一颗树,树上每个节点有
x
[
i
]
x[i]
x[i] 个饼干,这个节点中,吃掉一个饼干需要花
t
[
i
]
t[i]
t[i] 时间。走每条边需要花费
l
[
i
]
l[i]
l[i] 的时间。 首先人在
1
1
1 节点,也就是根节点处。
首先,
A
l
i
c
e
Alice
Alice 选择结束游戏或者继续往某个儿子走。 如果继续走,走到那个儿子后,
B
o
b
Bob
Bob 在当前节点的所有儿子节点中,删除一个儿子节点。然后仍然
A
l
i
c
e
Alice
Alice 再操作。
2
≤
n
≤
1
0
5
2\le n\le 10^5
2≤n≤105
1
≤
T
,
l
i
≤
1
0
18
1\le T,l_i\le 10^{18}
1≤T,li≤1018
1
≤
x
i
,
t
i
≤
1
0
6
1\le x_i,t_i\le 10^6
1≤xi,ti≤106
思路
考虑步骤解耦。我们最终走到某个节点
x
x
x ,花费的时间为
S
S
S,也就是说我们最终问你的是
T
−
2
S
T-2S
T−2S 的时间,对于这个节点到根的链上,你最多吃几个饼干,即答案为
d
p
[
x
]
dp[x]
dp[x]。
考虑最终的答案为
d
p
2
[
x
]
dp2[x]
dp2[x]。如果是根节点,儿子是任意走的,答案是
max
{
d
p
[
x
]
,
d
p
f
i
r
s
t
[
x
]
}
\max\{dp[x],dp_{first}[x]\}
max{dp[x],dpfirst[x]} 如果是叶子结点,答案就是
d
p
[
x
]
dp[x]
dp[x]。如果是中间的结点,答案是
max
{
d
p
[
x
]
,
d
p
s
e
c
o
n
d
[
x
]
}
\max\{dp[x],dp_{second}[x]\}
max{dp[x],dpsecond[x]},因为
B
o
b
Bob
Bob 会把最优的答案给删掉。
那我们的唯一难点是求
d
p
[
x
]
dp[x]
dp[x]。考虑到时间是
1
0
6
10^6
106,我们对于给定的饼干,最优吃法肯定是先吃花费时间最少的。我们可以用线段树去做,写一个线段树上二分即可。
Game 给定一个长度为
n
×
m
n\times m
n×m 的矩形网格纸 其中已经被切割了
k
k
k 刀,给出每刀的切割坐标(一定是水平或竖直切,端点都在格点处) 两个人轮流操作。每次切一刀,要求也是一定是水平或竖直切,端点都在格点处,且至少切到一条网格中的边(当然切割的地方不能是原来网格纸的最外面的四条边) 谁不能操作了,谁就输了。求先手胜负情况。如果先手必胜,输出一种先手的操作方案。
1
≤
n
,
m
≤
1
0
9
1\le n,m\le 10^9
1≤n,m≤109
0
≤
k
≤
1
0
5
0\le k\le 10^5
0≤k≤105
思路
非常经典的题目(但是我之前不会)。 首先,发现横切和竖切是独立的游戏,我们只考虑一个即可。
对于横切,相当于我们每次选择一个
y
y
y,然后切割这个
y
y
y 的线段集
S
S
S。 想到,我们至少要切割到一条边,且可以把所有的都切掉,相当于一个
N
i
m
Nim
Nim 博弈,这一堆中的石头我们至少拿一个,可以全拿光。这是一个很重要的转化! 所以我们发现,题目就是一个简单的
N
i
m
Nim
Nim 博弈。如果不考虑原有的
k
k
k 刀,就是
n
−
1
n-1
n−1 堆
m
m
m 个石头,以及
m
−
1
m-1
m−1 堆
n
n
n 个石头的博弈。 当然,切了一些只是让某些堆的石头少一点,本质是一样的。
现在一个问题,就是怎么输出先手必胜的方案。 假设游戏的
S
G
=
s
g
SG=sg
SG=sg,如果
s
g
>
0
sg>0
sg>0,先手是必胜的。我们需要找到某个堆,拿走一些石头,来让
s
g
=
0
sg=0
sg=0,怎么操作呢? 假设我们现在有一堆石头有
a
i
a_i
ai 个,我们拿走
x
x
x 个,这样我们的
S
G
=
s
g
⊕
a
i
⊕
x
=
0
SG=sg\oplus a_i \oplus x=0
SG=sg⊕ai⊕x=0 这里,唯一可行的答案即
x
=
s
g
⊕
a
i
x=sg \oplus a_i
x=sg⊕ai 当然,拿的石头数不能超过这堆石头的总数,所以我们需要满足
x
=
(
s
g
⊕
a
i
)
≤
a
i
x=(sg\oplus a_i) \le a_i
x=(sg⊕ai)≤ai 即可。
【解题报告】博弈专场(CF2200~2400)后五题F:Arpa and a game with Mojtaba | CF850C题意思路代码G:Tokitsukaze and Duel | CF1190C题意思路代码H:Warrior and Archer | CF594A题意思路代码F:Cookies | CF1099F题意思路代码J:Game | CF277C题意思路代码F:Arpa and a game with Mojtaba | CF850C题意Arpa and a game with