A.Bazoka and Mocha’s Array(思维)
题意:
摩卡喜欢数组,所以在出发前,巴祖卡送给她一个由 n n n个正整数组成的数组 a a a作为礼物。
现在,摩卡想知道数组 a a a在执行下面的操作若干次(可能是零次)后,能否以不递减的顺序排序:
- 将数组分成前缀和后缀两部分,然后交换这两部分。换句话说,让 a = x + y a=x+y a=x+y。然后,我们可以设置 a : = y + x a:=y+x a:=y+x。这里 + + +表示数组连接操作。
例如,如果是 a = [ 3 , 1 , 4 , 1 , 5 ] a=[3,1,4,1,5] a=[3,1,4,1,5],我们可以选择 x = [ 3 , 1 ] x=[3,1] x=[3,1]和 y = [ 4 , 1 , 5 ] y=[4,1,5] y=[4,1,5],满足 a = x + y a=x+y a=x+y。然后,我们可以设置 a : = y + x = [ 4 , 1 , 5 , 3 , 1 ] a:=y+x=[4,1,5,3,1] a:=y+x=[4,1,5,3,1]。我们还可以选择 x = [ 3 , 1 , 4 , 1 , 5 ] x=[3,1,4,1,5] x=[3,1,4,1,5]和 y = [ ] y=[\,] y=[],满足 a = x + y a=x+y a=x+y。然后,我们可以设置 a : = y + x = [ 3 , 1 , 4 , 1 , 5 ] a:=y+x=[3,1,4,1,5] a:=y+x=[3,1,4,1,5]。注意,我们不能选择 x = [ 3 , 1 , 1 ] x=[3,1,1] x=[3,1,1]和 y = [ 4 , 5 ] y=[4,5] y=[4,5],也不能选择 x = [ 1 , 3 ] x=[1,3] x=[1,3]和 y = [ 5 , 1 , 4 ] y=[5,1,4] y=[5,1,4],因为这两个选项都不满足 a = x + y a=x+y a=x+y。
分析:
x + y → y + x x+y→y+x x+y→y+x的操作其实就是把数组开头的一段移动到末尾。如果操作大于一次,可以合并成一个操作。因此最多操作一次。直接复制两边进行判定即可。
代码:
#include<bits/stdc++.h>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
vector<int> a(n + n);
for (int i = 0; i < n; i++) {
cin >> a[i];
a[i + n] = a[i];
}
bool flag = 0;
for (int i = 0; i < n; i++) {
bool fl = 1;
for (int j = i; j + 1 < i + n; j++) {
fl &= (a[j] <= a[j + 1]);
}
flag |= fl;
}
if (flag) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
}
return 0;
}
B.378QAQ and Mocha’s Array(思维)
题意:
摩卡喜欢数组,所以在出发前,378QAQ送给她一个由 n n n个正整数组成的数组 a a a作为礼物。
摩卡认为,如果存在两个数 i i i和 j j j( 1 ≤ i , j ≤ n 1\leq i,j\leq n 1≤i,j≤n, i ≠ j i\neq j i=j),使得对于所有的 k k k( 1 ≤ k ≤ n 1\leq k\leq n 1≤k≤n), a k a_k ak都能被 a i a_i ai或 a j a_j aj整除 † ^\dagger †,那么 a a a就是美丽的。
判断 a a a是否美丽。
† ^\dagger †如果存在整数 z z z使得 x = y ⋅ z x=y\cdot z x=y⋅z,那么 x x x可以被 y y y整除。
分析:
首先,最小的 a i a_i ai的 i i i一定要选,否则后面无法补偿。
因此我们已经确定了一个了。那么,我们把 m o d a i = 0 \mod a_i=0 modai=0的全部去掉,最小的就是另一个答案。最后再判断一次。
代码:
#include <bits/stdc++.h>
using namespace std;
int dp[10005];
string a;
long long mod=1e9+7;
int main(){
int n;
cin>>n;
cin>>a;
a='#'+a;
dp[0]=0;
dp[1]=1;
for(int i=2;i<=n;i++){
if((a[i-1]-'0')*10+(a[i]-'0')<=25&&(a[i-1]-'0')*10+(a[i]-'0')>=10){
dp[i]=(dp[i-1]+dp[i-2])%mod;
}else{
dp[i]=dp[i-1]%mod;
}
}
cout<<dp[n];
return 0;
}
C.Chamo and Mocha’s Array(区间)
题意:
摩卡喜欢数组,所以在出发前,查莫送给她一个由 n n n个正整数组成的数组 a a a作为礼物。
摩卡不喜欢包含不同数字的数组,所以摩卡决定用魔法来改变数组。摩卡可以执行下面的三步操作若干次(可能是零次):
- 选择索引 l l l和 r r r( 1 ≤ l < r ≤ n 1\leq l\lt r\leq n 1≤l<r≤n)
- 让 x x x成为子数组 [ a l , a l + 1 , … , a r ] [a_l,a_{l+1},\ldots,a_r] [al,al+1,…,ar]的中位数 † ^\dagger †。
- 设置所有值 a l , a l + 1 , … , a r a_l,a_{l+1},\ldots,a_r al,al+1,…,ar至 x x x。
假设初始值为 a = [ 1 , 2 , 3 , 4 , 5 ] a=[1,2,3,4,5] a=[1,2,3,4,5]:
- 如果摩卡在第一次操作中选择了 ( l , r ) = ( 3 , 4 ) (l,r)=(3,4) (l,r)=(3,4),那么 x = 3 x=3 x=3这个数组就会变成 a = [ 1 , 2 , 3 , 3 , 5 ] a=[1,2,3,3,5] a=[1,2,3,3,5]。
- 如果摩卡在第一次操作中选择了 ( l , r ) = ( 1 , 3 ) (l,r)=(1,3) (l,r)=(1,3),那么 x = 2 x=2 x=2,数组将变为 a = [ 2 , 2 , 2 , 4 , 5 ] a=[2,2,2,4,5] a=[2,2,2,4,5]。
摩卡会一直执行操作,直到数组只包含相同的数字。摩卡想知道这个数字的最大可能值是多少。
† ^\dagger †在长度为 m m m的数组 b b b中,中位数是按非递减顺序对元素排序后占据位置编号 ⌊ m + 1 2 ⌋ \lfloor\frac{m+1}{2}\rfloor ⌊2m+1⌋的元素。例如, [ 3 , 1 , 4 , 1 , 5 ] [3,1,4,1,5] [3,1,4,1,5]的中位数是 3 3 3, [ 5 , 25 , 20 , 24 ] [5,25,20,24] [5,25,20,24]的中位数是 20 20 20。
分析:
考虑到如果有两个相同的连续元素了,一定可以通过操作把这个序列全部变为这个元素。
怎么出现两个相同元素呢?容易发现操作 > 3 \gt 3 >3的长度的区间是不优的,一定可以拆成更多的小区间。只需要判断 2 , 3 2,3 2,3长度的即可。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N = 1e5 + 10;
int a[N];
void solve() {
int n;
cin >> n;
a[0] = a[n + 1] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int mx = 0;
for (int i = 1; i <= n; i++) {
if (a[i - 1] >= a[i] || a[i + 1] >= a[i]) {
mx = max(mx, a[i]);
}
if (i >= 3) {
if (a[i - 2] >= a[i]) {
mx = max(mx, a[i]);
}
}
if (i + 2 <= n) {
if (a[i + 2] >= a[i]) {
mx = max(mx, a[i]);
}
}
}
cout << mx << endl;
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
D.Paint the Tree(搜索、贪心)
题意:
378QAQ有一棵有 n n n个顶点的树。初始时,所有顶点都是白色的。
树上有两颗棋子,分别叫做 P A P_A PA和 P B P_B PB。 P A P_A PA和 P B P_B PB最初分别位于顶点 a a a和 b b b。在一个步骤中,378QAQ将依次执行以下操作:
- 将 P A P_A PA移动到邻近顶点。如果目标顶点是白色,那么这个顶点将被涂成红色。
- 将 P B P_B PB移动到相邻的顶点。如果目标顶点为红色,则此顶点将被涂抹为蓝色。
最初,顶点 a a a会被涂成红色。如果是 a = b a=b a=b,顶点 a a a将被涂成蓝色。请注意,每一步都必须移动两个棋子。任何时候都可以有两个棋子位于同一个顶点上。
378QAQ想知道将所有顶点都涂成蓝色所需的最少步数。
分析:
P A P_A PA与 P B P_B PB在一起,我们每次可以移动一下 P A P_A PA,然后 P B P_B PB移到同样的位置,这样就要移动 2 n − 2 − R 2n−2−R 2n−2−R,其中 R R R为离原来的点最远的节点的距离。
假设只需要 P B P_B PB,这个答案是显然的,有了 P A P_A PA,限制条件增加,答案不降,因此正确。
考虑稍特殊的情况,两棋子距离为偶数,为了第二个棋子尽快开始遍历染色,显然需要两个棋子快速走到一起。
考虑距离为奇数,显然首先仍然为快速接近。接着考虑怎么走才最贪心,可以红走一次,蓝跟到上一个红的地方,这样二者可相邻,且染色有效,显然要染色都要染成红色,但是最后一次蓝色少走一步,所以要再加上一。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL N = 1000010;
LL n, a, b, ans;
struct st {
LL v, ne;
} sd[N];
LL h[N];
LL inn;
void add(LL u, LL v) {
sd[++inn].v = v;
sd[inn].ne = h[u];
h[u] = inn;
}
LL fa[N], d[N];
void dfs(LL t) {
d[t] = d[fa[t]] + 1;
for (LL i = h[t]; i; i = sd[i].ne) {
if (sd[i].v != fa[t]) {
fa[sd[i].v] = t;
dfs(sd[i].v);
}
}
}
LL del(LL t, LL fa) {
LL res = -1;
for (LL i = h[t]; i; i = sd[i].ne) {
if (sd[i].v != fa) {
res = max(res, del(sd[i].v, t));
}
}
return res + 1;
}
void cal() {
inn += 10;
while (inn) {
sd[inn--] = {0, 0};
}
for (LL i = 0; i <= n + 10; i++) {
h[i] = fa[i] = d[i] = 0;
}
ans = 0;
}
void solve() {
cin >> n;
cin >> a >> b;
fa[a] = a;
for (LL i = 1; i < n; i++) {
LL u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
fa[a] = 0;
d[0] = -1;
dfs(a);
ans += d[b] / 2;
ans += 2 * n - 2;
LL z;
z = d[b];
for (LL i = 1; i <= z / 2; i++) {
b = fa[b];
}
if (z & 1) {
ans -= del(fa[b], 0) - 1;
} else {
ans -= del(b, 0);
}
cout << ans << endl;
cal();
}
int main() {
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
E.Chain Queries(树)
题意:
给你一棵由 n n n个顶点组成的树,这些顶点的编号从 1 1 1到 n n n不等。最初,所有顶点都被染成白色或黑色。
要求您执行 q q q次查询:
- “u”-切换顶点 u u u的颜色(如果顶点为白色,则将其变为黑色,反之亦然)。
每次查询后,您都应回答所有黑色顶点是否形成一条链。也就是说,是否存在两个黑色顶点,使得它们之间的简单路径经过所有黑色顶点,并且只经过黑色顶点。特别的,如果只有一个黑色顶点,它们会算形成一条链。如果没有黑色顶点,则它们不构成链。
分析:
维护儿子黑色节点的个数为 0 , 1 , 2 , ≥ 3 0,1,2,≥3 0,1,2,≥3的个数即可。
考虑链的特征,以下情况均满足才合法:
- 没有一个节点有三个黑色儿子。
- 最多一个节点有两个黑色儿子,且该节点必须是黑色。
- 只有一个黑色节点的父亲是白色,该黑色节点是 L C A LCA LCA。
每次修改只需要更改自己的颜色,父亲对黑色儿子的记录,记录儿子黑色节点的个数的节点个数的桶,有两个黑色儿子的点集。
代码:
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const LL mod = 1000000007;
const LL N = 2e5 + 10;
LL a[N], n, m, tot;
set<LL> q;
vector<LL> v[N];
LL cnt[N], ch[N], fa[N], col;
void dfs(LL x, LL tmp) {
LL sum = 0;
fa[x] = tmp;
if (!a[fa[x]] && a[x])
col++;
for (auto y: v[x]) {
if (y == fa[x])
continue;
if (a[y])
sum++;
dfs(y, x);
}
ch[x] = sum;
cnt[min(sum, 3ll)]++;
if (ch[x] == 2)
q.insert(x);
}
void solve() {
col = tot = 0;
q.clear();
cin >> n >> m;
cnt[0] = cnt[1] = cnt[2] = cnt[3] = ch[0] = 0;
for (int i = 1; i <= n; i++) {
v[i].clear();
cin >> a[i];
tot += a[i];
ch[i] = fa[i] = 0;
}
for (int i = 1; i < n; i++) {
LL x, y;
cin >> x >> y;
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1, 0);
for (int i = 1; i <= m; i++) {
LL x;
cin >> x;
if (a[x]) {
cnt[min(ch[fa[x]], 3ll)]--;
if (ch[fa[x]] == 2)
q
.erase(fa[x]);
ch[fa[x]]--;
if (ch[fa[x]] == 2)
q.insert(fa[x]);
cnt[min(ch[fa[x]], 3ll)]++;
col += ch[x] - (a[fa[x]] == 0);
tot--;
} else {
cnt[min(ch[fa[x]], 3ll)]--;
if (ch[fa[x]] == 2)
q.erase(fa[x]);
ch[fa[x]]++;
if (ch[fa[x]] == 2)
q.insert(fa[x]);
cnt[min(ch[fa[x]], 3ll)]++;
col -= ch[x] - (a[fa[x]] == 0);
tot++;
}
a[x] ^= 1;
if (tot < 1) {
cout << "No" << endl;
continue;
}
if (cnt[3]) {
cout << "No" << endl;
continue;
}
if (cnt[2] > 1) {
cout << "No" << endl;
continue;
}
if (col != 1) {
cout << "No" << endl;
continue;
}
if (cnt[2] == 1 && a[fa[*q.begin()]]) {
cout << "No" << endl;
continue;
}
cout << "Yes" << endl;
}
}
int main() {
int t;
cin >> t;
while (t--)
solve();
return 0;
}
F.Set(分治)
题意:
将有限自然数集 T ⊆ { 0 , 1 , 2 , … } T\subseteq\{0,1,2,\ldots\} T⊆{0,1,2,…}的二进制编码定义为 f ( T ) = ∑ i ∈ T 2 i f(T)=\sum\limits_{i \in T}2^i f(T)=i∈T∑2i。例如, f ( { 0 , 2 } ) = 2 0 + 2 2 = 5 f(\{0,2\})=2^0+2^2=5 f({0,2})=20+22=5和 f ( { } ) = 0 f(\{\})=0 f({})=0。请注意, f f f是从所有此类集合到所有非负整数的双射。因此, f − 1 f^{-1} f−1也是有定义的。
给你一个整数 n n n和 2 n − 1 2^n-1 2n−1集合 V 1 , V 2 , … , V 2 n − 1 V_1,V_2,\ldots,V_{2^n-1} V1,V2,…,V2n−1。
请找出满足以下约束条件的所有集合 S S S:
- S ⊆ { 0 , 1 , … , n − 1 } S\subseteq\{0,1,\ldots,n-1\} S⊆{0,1,…,n−1}。注意 S S S可以是空的。
- 对于所有非空子集 T ⊆ { 0 , 1 , … , n − 1 } T\subseteq\{0,1,\ldots,n-1\} T⊆{0,1,…,n−1}, ∣ S ∩ T ∣ ∈ V f ( T ) |S\cap T|\in V_{f(T)} ∣S∩T∣∈Vf(T)。
分析:
考虑枚举从 0 0 0到 n − 1 n-1 n−1的每个数字是否被 S S S包含,当枚举了前 x x x个数字后,发现只有 2 n − x 2^{n-x} 2n−x个约束。
考虑枚举从 0 0 0到 n − 1 n-1 n−1的每个数字是否被 S S S所包含。假设当前枚举到 i i i,在剩余的约束条件中, T 1 T_1 T1和 T 2 T_2 T2是两个集合,它们之间唯一的区别是是否包含 i i i( T 1 T_1 T1包含 i i i):
- S S S包含 i i i。我们可以将 T 1 T_1 T1和 T 2 T_2 T2合并为新的约束条件: T ′ T' T′和 v T ′ = ( v T 1 » 1 ) v_{T'}=(v_{T_1}»1) vT′=(vT1»1)& v T 2 v_{T_2} vT2。
- S S S不包含 i i i。我们可以将 T 1 T_1 T1和 T 2 T_2 T2合并为新的约束条件: T ′ T' T′和 v T ′ = v T 1 v_{T'}=v_{T_1} vT′=vT1& v T 2 v_{T_2} vT2。
当枚举达到一个新数字时,就可以快速合并约束。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 24, S = (1 << 20) + 5;
int n, f[N][S] = {};
vector<int> ans;
inline void dfs(int s = 0, int i = 0) {
if (i < n) {
int m = 1 << (n - i - 1);
for (int t = 0; t < m; t++)
f[i + 1][t] = f[i][t] & f[i][m | t];
dfs(s << 1, i + 1);
for (int t = 0; t < m; t++)
f[i + 1][t] = f[i][t] & (f[i][m | t] >> 1);
dfs(s << 1 | 1, i + 1);
} else if (f[n][0] & 1)
ans.push_back(s);
}
int main() {
ios::sync_with_stdio(false);
cin >> n;
f[0][0] = 1;
for (int s = 1; s < (1 << n); s++)
cin >> f[0][s];
dfs();
cout << int(ans.size()) << endl;
for (int s: ans)
cout << s << endl;
return 0;
}
赛后交流
在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。
群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。