2022.07.28
1582D. Vupsen, Pupsen and 0
题目:
题解:
考虑确定
b
i
b_i
bi 和
b
j
b_j
bj 使得
a
i
×
b
i
+
a
j
×
b
j
=
0
a_i\times b_i+a_j\times b_j=0
ai×bi+aj×bj=0,当
b
i
=
a
j
b_i=a_j
bi=aj 且
b
j
=
−
a
i
b_j=-a_i
bj=−ai 是一种合法的方案。
当 n n n 为偶数时, ∑ i = 1 n ∣ b i ∣ = ∑ i = 1 n ∣ a i ∣ ≤ 1 0 9 \sum\limits_{i=1}^n |b_i|=\sum\limits_{i=1}^n |a_i|\leq 10^9 i=1∑n∣bi∣=i=1∑n∣ai∣≤109是显然的。
当
n
n
n 为奇数时,两两匹配会多出一个数,这个数与任意一组构成一个三数的组。
设这三个数为
a
i
,
a
j
,
a
k
a_i,a_j,a_k
ai,aj,ak ,可以证明的是三个数中任选两个数,至少有一种情况下的两个数之和不为
0
0
0(证明如下)。如此就满足了
b
b
b 中任意数都不为
0
0
0 的要求了
假设
a
i
+
a
j
≠
0
a_i+a_j\neq 0
ai+aj=0,那么
b
i
=
b
j
=
a
k
,
b
k
=
−
(
a
i
+
a
j
)
b_i=b_j=a_k, b_k=-(a_i+a_j)
bi=bj=ak,bk=−(ai+aj)
因为
n
<
1
0
5
n<10^5
n<105,所以在极限最坏情况下,
∑
i
=
1
n
∣
b
i
∣
=
∑
i
=
1
n
∣
a
i
∣
+
max
{
a
i
}
=
∑
i
=
1
n
∣
a
i
∣
+
1
0
4
=
1
0
9
\sum\limits_{i=1}^n |b_i|=\sum\limits_{i=1}^n |a_i|+\max\{a_i\}=\sum\limits_{i=1}^n |a_i|+10^4=10^9
i=1∑n∣bi∣=i=1∑n∣ai∣+max{ai}=i=1∑n∣ai∣+104=109 。
本题值域为 [ − 1 0 4 , − 1 ] [-10^4,-1] [−104,−1] 和 [ 1 , 1 0 4 ] [1,10^4] [1,104],而数量为 1 0 5 10^5 105,在这种情况下必然是存在绝对值相同的元素,可以降低 b i b_i bi 的绝对值,但是在此不做细致的讨论了。
关于证明:
题目中已说明
a
a
a 中任意的数都不等于
0
0
0,下面采用反证法证明
a
i
+
a
j
=
0
a_i+a_j=0
ai+aj=0
a
j
+
a
k
=
0
a_j+a_k=0
aj+ak=0
a
i
+
a
k
=
0
a_i+a_k=0
ai+ak=0
推导出:
a
i
=
a
j
=
a
k
=
0
a_i=a_j=a_k=0
ai=aj=ak=0,与题意不符,所以不存在三个数中任选两个数的和都为
0
0
0 。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010;
int a[N], n;
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
int fir = 1;
if (n & 1) {
if (a[1] + a[2] != 0) {
int v1 = a[1] + a[2];
int v2 = a[3];
a[1] = a[2] = v2, a[3] = -v1;
} else if (a[1] + a[3] != 0) {
int v1 = a[1] + a[3];
int v2 = a[2];
a[1] = a[3] = v2, a[2] = -v1;
} else if (a[2] + a[3] != 0) {
int v1 = a[2] + a[3];
int v2 = a[1];
a[2] = a[3] = v2, a[1] = -v1;
}
fir += 3;
}
for (int k = fir; k + 1 <= n; k += 2) {
int v1 = a[k], v2 = a[k + 1];
if (v1 * v2 > 0) a[k] = v2, a[k + 1] = -v1;
else a[k] = -v2, a[k + 1] = v1;
}
for (int i = 1; i <= n; ++i) printf("%d%c", a[i], " \n"[i == n]);
}
int main()
{
int T = 1;
scanf("%d", &T);
for (int i = 1; i <= T; ++i) {
solve();
}
return 0;
}
1583D. Omkar and the Meaning of Life
题目:
题解:
由于返回的是第一个和出现两次的最早索引,可以考虑小于
p
n
p_n
pn 的数的个数来确定
p
n
p_n
pn 的大小,并且通过其他数与
p
n
p_n
pn 的大小关系来确定对应的值。
首先设定 a n = 1 a_n=1 an=1,当 a 1 a_1 a1 到 a n − 1 a_{n-1} an−1 都相同, s 1 s_1 s1 到 s n − 1 s_{n-1} sn−1 必然不同,因此返回的 k k k 必然是存在 s k = s n s_k=s_n sk=sn的。将 a 1 a_1 a1 到 a n − 1 a_{n-1} an−1 依次设定为 2 − n 2-n 2−n,找到比 p n p_n pn 小的数,并且设定这些数与 p n p_n pn 的差,统计小于 p n p_n pn 数的个数 c n t cnt cnt,那么 p n p_n pn 就是 c n t + 1 cnt+1 cnt+1。
其次设定 a 2 a_2 a2 到 a n a_n an 为 1 1 1, a 1 a_1 a1 依次设定为 2 − n 2-n 2−n,找到比 p n p_n pn 大的数,并且设定这些数与 p n p_n pn 的差。
最后通过这些差确定所有的 p i p_i pi
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 110;
int a[N], n;
int ch[N];
int res;
int ask(int a, int b) {
printf("?");
for (int i = 1; i < n; ++i) printf(" %d", a);
printf(" %d", b);
puts("");
fflush(stdout);
scanf("%d", &res);
return res;
}
void solve() {
scanf("%d", &n);
a[n] = 1;
for (int i = 2; i <= n; ++i) {
int k = ask(i, 1);
if (!k) break;
a[n] += 1;
ch[k] = 1 - i;
}
for (int i = 2; i <= n; ++i) {
int k = ask(1, i);
if (!k) break;
ch[k] = i - 1;
}
printf("!");
for (int i = 1; i <= n; ++i) printf(" %d", a[n] + ch[i]);
puts("");
fflush(stdout);
}
int main()
{
int T = 1;
// scanf("%d", &T);
for (int i = 1; i <= T; ++i) {
solve();
}
return 0;
}
1592C. Bakry and Partitioning
题目:
题解:
首先求出所有点的异或和
- 当异或和为 0 0 0,断开任意一个叶子即可
- 当异或和为
s
s
s,那么必然是分成奇数个异或和为
s
s
s 的连通分量
这里在 d f s dfs dfs 时,找到一个连通分量的异或和为 s s s 时,统计连通分量异或和为 s s s 的个数,并清空临时异或值。- 当 k = 2 k=2 k=2 时,只能拆成两个连通分量,必然不存在合法方案
- 当 k > 2 k>2 k>2 时,当异或和为 s s s 的连通分量为 c n t ( c n t ≥ 3 ) cnt(cnt\geq 3) cnt(cnt≥3) 个,这个 c n t cnt cnt 必然是个奇数,就可以拆成两个由 1 1 1个异或和为 s s s 的连通分量和一个由 c n t − 2 cnt-2 cnt−2 个异或和为 s s s 的连通分量构成。如下图所示,每个点代表一个连通分量。
![](https://img-blog.csdnimg.cn/01f914fda1b3478a9eea141aea4a8e25.png)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010, M = N << 1;
int h[N], e[M], ne[M], idx;
int a[N], n, k;
int sum, cnt;
bool ok;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u, int fa) {
int s = a[u];
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (v != fa) {
s ^= dfs(v, u);
}
}
if (s == sum) cnt += 1, s = 0;
return s;
}
void solve() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), h[i] = -1;
idx = 0; cnt = 0;
for (int i = 1, a, b; i < n; ++i) {
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
sum = 0;
for (int i = 1; i <= n; ++i) sum ^= a[i];
if (sum == 0) {
puts("YES");
} else {
if (k == 2) {
puts("NO");
return ;
}
dfs(1, -1);
if (cnt >= 3) puts("YES");
else puts("NO");
}
}
int main()
{
int T = 1;
scanf("%d", &T);
for (int i = 1; i <= T; ++i) {
solve();
}
return 0;
}