A
题目描述
现有一个有nnn个元素的序列a=[a1,a2,⋯ ,an]a=[a_1,a_2,\cdots, a_n]a=[a1,a2,⋯,an],定义这个序列的价值为∑i=1ni×ai\sum_{i=1}^{n}i\times a_i∑i=1ni×ai。空序列的价值为000。
先给你一个长度为nnn的序列aaa,求aaa中所有子集价值的异或和,要求子集中元素的相对位置保持不变。
输入
第一行一个整数nnn
接下来一行有n个非负整数表示a1,a2,⋯ ,ana_1, a_2, \cdots, a_na1,a2,⋯,an
输出
一个整数表示所有子集的异或和
样例1
输入
2
1 2
输出
6
数据规模
1≤n≤201\leq n\leq201≤n≤20,1≤a≤1051\leq a\leq10^51≤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], 其价值为value0=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]其价值为value1=1×1+2×2+3×3+4×5=value0+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=[a1,a2,⋯ ,an]a = [a_{1}, a_{2}, \cdots , a_{n}]a=[a1,a2,⋯,an],定义其价值为 ∑i=1nai⊕i\sum_{i=1}^{n}a_{i} \oplus i∑i=1nai⊕i
给出这样一个序列,求其所有排列的价值 viv_{i}vi 的或 v1∣v2∣⋯∣vn−1∣vnv_{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;
}
文章讨论了计算给定序列子集异或和的方法,涉及递归策略,以及全排列价值的求解,通过固定元素顺序简化问题。

946

被折叠的 条评论
为什么被折叠?



