A
题目描述
现有一个有 n n n个元素的序列 a = [ a 1 , a 2 , ⋯ , a n ] a=[a_1,a_2,\cdots, a_n] a=[a1,a2,⋯,an],定义这个序列的价值为 ∑ i = 1 n i × a i \sum_{i=1}^{n}i\times a_i ∑i=1ni×ai。空序列的价值为 0 0 0。
先给你一个长度为 n n n的序列 a a a,求 a a a中所有子集价值的异或和,要求子集中元素的相对位置保持不变。
输入
第一行一个整数 n n n
接下来一行有n个非负整数表示 a 1 , a 2 , ⋯ , a n a_1, a_2, \cdots, a_n a1,a2,⋯,an
输出
一个整数表示所有子集的异或和
样例1
输入
2
1 2
输出
6
数据规模
1 ≤ n ≤ 20 1\leq n\leq20 1≤n≤20, 1 ≤ a ≤ 1 0 5 1\leq a\leq10^5 1≤a≤105
解题思路
本题需要明确有哪些序列,如何求得序列价值,以及如何存储价值并异或得到异或和。
由于不同元素的相对位置是固定的,因此可以说一个序列是另一个序列“派生”来的。示例如下:有序列
[
1
,
2
,
3
,
4
,
5
,
6
]
[1,2,3,4,5,6]
[1,2,3,4,5,6],我们选取一个子集
[
1
,
2
,
3
]
[1,2,3]
[1,2,3], 其价值为
v
a
l
u
e
0
=
1
×
1
+
2
×
2
+
3
×
3
=
14.
value_0=1\times1+2\times2+3\times3=14.
value0=1×1+2×2+3×3=14. 同时我们可以选取另一个子集
[
1
,
2
,
3
,
5
]
[1,2,3,5]
[1,2,3,5]其价值为
v
a
l
u
e
1
=
1
×
1
+
2
×
2
+
3
×
3
+
4
×
5
=
v
a
l
u
e
0
+
4
×
5.
value_1=1\times1+2\times2+3\times3+4\times5=value_0+4\times5.
value1=1×1+2×2+3×3+4×5=value0+4×5.因此我们可以不用重复从头计算派生子集的价值,而是直接从原子集的基础上加上一个值得到派生子集的价值。这样可以降低算法的时间复杂度。当然,本题的数据并没有限制算法的时间复杂度,如果直接计算每一个子集的价值然后异或也是完全可以AC的。
代码(此代码已使用,请勿直接抄袭)
#include<iostream>
using namespace std;
#define ll long long
ll SUM = 0;
void dg_value(int loc,ll a[],int deep, int n,ll save_value) {
if (deep > n||n-loc==0)return;
for (int i = loc; i < n; i++) {
save_value += a[i]*deep;
SUM = SUM ^ save_value;
dg_value(i+1,a, deep + 1, n, save_value);
save_value -= a[i] * deep;
}
}
int main() {
int n;
cin >> n;
ll a[22];
for (int i = 0; i < n; i++)cin >> a[i];
dg_value(0,a, 1, n, 0);
cout << SUM;
}
B
题目描述
现有一个有n 个元素的序列
a
=
[
a
1
,
a
2
,
⋯
,
a
n
]
a = [a_{1}, a_{2}, \cdots , a_{n}]
a=[a1,a2,⋯,an],定义其价值为
∑
i
=
1
n
a
i
⊕
i
\sum_{i=1}^{n}a_{i} \oplus i
∑i=1nai⊕i
给出这样一个序列,求其所有排列的价值
v
i
v_{i}
vi 的或
v
1
∣
v
2
∣
⋯
∣
v
n
−
1
∣
v
n
v_{1}| v_{2} | \cdots | v_{n-1} | v_{n}
v1∣v2∣⋯∣vn−1∣vn
其中
∣
|
∣ 为位运算或操作,
⊕
\oplus
⊕ 为位运算异或操作
输入
输入的第一行是一个整数 n (2<=n<=10),表示需排列的数的个数。接下来一行是 n 个整数,数的范围是 0 到 100000,每两个相邻数据间用一个空格分隔。
输出
一个整数,代表所有排列价值的或。
样例1
输入
3
1 2 3
输出
6
解题思路
本题的关键在于求得全排列。为了更好的帮助读者理解求全排列算法的思路,我们由易到难列举如下几个例子:
- 对于序列 [ 1 , 2 ] [1,2] [1,2],固定1为第一位,有序列 [ 1 , 2 ] [1,2] [1,2];固定2为第一位,有序列 [ 2 , 1 ] [2,1] [2,1]。等同于把1和2进行交换。
- 对于序列 [ 1 , 2 , 3 ] [1,2,3] [1,2,3],固定1为第一位,有序列 [ 1 , 2 , 3 ] [1,2,3] [1,2,3], [ 1 , 3 , 2 ] [1,3,2] [1,3,2];固定2为第一位,有序列 [ 2 , 1 , 3 ] [2,1,3] [2,1,3], [ 2 , 3 , 1 ] [2,3,1] [2,3,1];固定3为第一位,有序列 [ 3 , 2 , 1 ] [3,2,1] [3,2,1], [ 3 , 1 , 2 ] [3,1,2] [3,1,2]。当固定第一位时,相当于只用考虑除第一位外的剩余两位。此时问题转化为序列中有两个元素的问题。
- 对于序列 [ 1 , 2 , 3 , 4 ] [1,2,3,4] [1,2,3,4],还是分别固定1,2,3,4为第一位,剩余的三位元素按照上面“2.”的方式处理。
因此,对于递归的每一层,我们只需要分别让序列的每一位和第一位交换(此处“每一位”包含第一位元素),得到的序列就可以不再考虑第一位元素,而是只考虑后面的序列。而后面的序列仍可以按照刚才的方式处理。直到剩下的数组只有一个元素的时候,我们就得到了一个排列结果,这也就是递归返回的条件。按照这种方式就可以求得所有的排列。
代码(此代码已使用,请勿直接抄袭)
#include<iostream>
using namespace std;
#define ll long long
ll result = 0;
ll value(int a[],int n) {
ll tmp = 0;
for (int i = 0; i < n; i++) {
tmp += a[i]^(i+1);
}
return tmp;
}
template<class T>
void sweap(T& a,T& b) {
T tmp;
tmp = a;
a = b;
b = tmp;
}
void dg(int a[], int n,int deep) {
if (deep == n) {
result |= value(a, n);
}
for (int i = deep; i < n; i++) {
sweap(a[i], a[deep]);
dg(a, n, deep + 1);
sweap(a[i], a[deep]);
}
return;
}
int main() {
int a[100];
int n;
cin >> n;
for (int i = 0; i < n; i++)cin >> a[i];
dg(a, n, 0);
cout << result;
}