A. 二进制热身
https://codeforces.com/contest/2108/problem/A
每次测试的时间限制:1 秒钟
每次测试的内存限制:256 兆字节
输入:标准输入
输出:标准输出
对于长度为
n
n
n
∗
^{\text{∗}}
∗ 的排列
p
p
p,我们定义了函数:
f
(
p
)
=
∑
i
=
1
n
∣
p
i
−
i
∣
f(p) = \sum_{i=1}^{n} \lvert p_i - i \rvert
f(p)=i=1∑n∣pi−i∣
给你一个数字
n
n
n。你需要计算当考虑从
1
1
1 到
n
n
n 的所有可能排列时,函数
f
(
p
)
f(p)
f(p) 可以取多少个不同的**值。
∗ ^{\text{∗}} ∗ 长度为 n n n 的排列是由 n n n 个不同的整数组成的数组,这些整数从 1 1 1 到 n n n 按任意顺序排列。例如, [ 2 , 3 , 1 , 5 , 4 ] [2,3,1,5,4] [2,3,1,5,4] 是一个排列,但 [ 1 , 2 , 2 ] [1,2,2] [1,2,2] 不是一个排列( 2 2 2 在数组中出现了两次), [ 1 , 3 , 4 ] [1,3,4] [1,3,4] 也不是一个排列( n = 3 n=3 n=3,但数组中有 4 4 4)。
输入
每个测试包含多个测试用例。第一行包含测试用例的数量 t t t ( 1 ≤ t ≤ 100 1 \le t \le 100 1≤t≤100)。测试用例说明如下。
每个测试用例的第一行都包含一个整数 n n n ( 1 ≤ n ≤ 500 1 \leq n \leq 500 1≤n≤500) —— 排列组合中的数字个数。
输出
对于每个测试用例,输出一个整数 —— 在给定的排列长度下,函数 f ( p ) f(p) f(p) 的不同值的个数。
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
int n;
cin >> n;
if (n == 1) {
cout << "1\n";
return;
}
ll max_f = 0;
//1-max_f内所有偶数个数
for (int i = 1; i <= n; i++) {
max_f += abs(n - i + 1 - i);
}
if (n == 2) {
cout << "2\n";
} else {
cout << (max_f / 2) + 1 << "\n";
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while(t--){
solve();
}
return 0;
}
B.SUMdamental 分解
https://codeforces.com/contest/2108/problem/B
每次测试的时间限制: 1 秒
每次测试的内存限制: 256 兆字节
输入: 标准输入
输出: 标准输出
问题描述
在你最近的一次生日上,你最好的朋友莫里斯给了你一对数字
n
n
n 和
x
x
x,并要求你构造一个长度为
n
n
n 的正数数组
a
a
a,使得:
a
1
⊕
a
2
⊕
⋯
⊕
a
n
=
x
a_1 \oplus a_2 \oplus \cdots \oplus a_n = x
a1⊕a2⊕⋯⊕an=x
(
⊕
\oplus
⊕ 表示按位异或运算)
这个任务对你来说似乎太简单了,因此你决定给莫里斯一个回礼:在所有满足条件的数组中,构造一个元素之和最小的数组。由于直接写出数组太费时间,你只需要计算并输出这个最小和。
输入格式
每个测试包含多个测试用例。第一行包含测试用例的数量 t t t ( 1 ≤ t ≤ 1 0 4 1 \leq t \leq 10^4 1≤t≤104)。
每个测试用例由一行组成,包含两个整数 n n n 和 x x x ( 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1≤n≤109, 0 ≤ x ≤ 1 0 9 0 \leq x \leq 10^9 0≤x≤109)。
输出格式
对于每个测试用例,输出满足条件数组的最小元素和。如果没有这样的数组存在,输出 − 1 -1 −1。
解题思路
特殊情况处理
-
当 n = 1 n=1 n=1 且 x = 0 x=0 x=0 时
- 无法构造满足条件的数组,因为唯一的元素必须是正数,而任何正数的异或结果都不可能是0
- 输出 − 1 -1 −1
-
当 x = 0 x=0 x=0 时
- 需要构造异或结果为0的数组
- 如果
n
n
n 是偶数:
- 可以构造数组 [ 1 , 1 , . . . , 1 ] [1, 1, ..., 1] [1,1,...,1]( n n n 个1)
- 因为 1 ⊕ 1 = 0 1 \oplus 1 = 0 1⊕1=0,且 n n n 个1的异或结果为0
- 和为 n n n
- 如果
n
n
n 是奇数:
- 需要额外增加两个元素(如2和3)
- 因为奇数个1的异或结果为1
- 和为 n + 3 n + 3 n+3
一般情况处理
-
计算 x x x的二进制中1的个数( p c pc pc)
- 使用
__builtin_popcount(x)
计算
- 使用
-
如果 c n t ≥ n cnt \geq n cnt≥n
- 可以直接输出 x x x
- 因为可以用 x x x作为其中一个元素,其余用1填充
-
构造最小和数组
- 初始化答案 a n s = 0 ans = 0 ans=0
- 特殊情况处理:
- 如果 x = 1 x=1 x=1且 n n n是偶数,需要额外加2
- 从 n n n中减去 c n t cnt cnt(因为 x x x贡献了 c n t cnt cnt个1)
- 处理剩余的
n
n
n:
- 每次处理2个元素(加2到 a n s ans ans,减2从 n n n)
- 如果最后剩下1个元素( n = 1 n=1 n=1),再加2
- 最终结果为 a n s + x ans + x ans+x
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
ll n, x;
cin >> n >> x;
if (n==1 && x==0) {
cout << -1 << "\n";
return;
}
if (x==0) {
if (n%2==0) {
cout << n << "\n";
} else {
cout << n+3 << "\n";
}
return;
}
ll cnt=__builtin_popcount(x);
if (cnt>=n) {
cout << x << "\n";
return;
}
ll ans=0;
if (x==1 && n%2==0) {
ans+=2;
}
n-=cnt;
while (n>=2) {
ans+=2;
n-=2;
}
if (n==1) {
ans+=2;
}
cout << ans+x << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}