Codeforces Round #845(div2)
A. Everybody Likes Good Arrays!
题目大意:
长度为 n n n 的正整数数组 A A A 若是满足相邻两个元素奇偶性不同,则称为好数组。
每次操作可以把 A A A 数组任意两个相邻的元素删除,并且在原位置插入它们的乘积。
问最少需要几次操作。
n ≤ 100 n\le100 n≤100
解答:
我们需要知道:
- 奇数与奇数相乘还是奇数,偶数与偶数相乘还是偶数
所以,如果数组里面有连续的 n n n 个相同奇偶性的元素,就需要 n − 1 n-1 n−1 次合并。扫描一次数组,找到所有连续的相同的奇偶性的元素,并且在答案上加上 n − 1 n-1 n−1 即可。
小技巧:
由于我们只考虑奇偶性,所以我们读入数组的时候只需要读入奇偶性即可,即设置数组为 a[i]&1
单词积累:
adjacent 相邻的 parity 奇偶性 trivially 非常好的 operation 操作 product 乘积
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int N = 110;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n);
for (auto& it : a) {
cin >> it;
it &= 1;
}
int ans = 0;
for (int i = 1; i < n; i++) {
ans += a[i] == a[i - 1];
}
cout << ans << endl;
}
return 0;
}
B. Emordnilap
题目大意:
给定正整数 n n n ,对于 n n n 的某个排列 p p p ,构造序列 p + r e v e r s e ( p ) p+reverse(p) p+reverse(p) ,记该序列逆序对数量 f ( p ) f(p) f(p)。
对于 n n n 的全排列,对他们的 f ( p ) f(p) f(p) 求和,并且对 1 e 9 + 7 1e9+7 1e9+7 取模。
n ≤ 1 e 5 n\le1e5 n≤1e5
解答:
我们需要知道:
- n n n 的全排列个数为 n ! n! n!
- 取模的乘法运算: ( a × b ) % p = ( ( a % p ) × ( b % p ) ) % p (a\times b)\%p=((a\%p)\times(b\%p))\%p (a×b)%p=((a%p)×(b%p))%p
- 1 e 9 + 7 1e9+7 1e9+7 过大,两个这样大的数相乘会爆 i n t int int,所以需要开 l o n g l o n g long long longlong
对于序列中任意两个数字,只考虑它们的逆序对,一定有 2 2 2 个。所以单个序列的所有逆序对数量为 C n 2 ∗ 2 = n ( n − 1 ) C_n^2*2=n(n-1) Cn2∗2=n(n−1) 个,那么所有的排列的逆序对就是 n ! ∗ n ( n − 1 ) n!*n(n-1) n!∗n(n−1) 个。
单词积累:
permutation 排列 distinct 独一无二的 arbitrary 任意的 concatenated 连接 inversions 逆序对
indice 下标 remainder 余数 guaranteed 保证
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100010, mod=1e9+7;
typedef long long LL;
LL a[N];
int main() {
a[1] = 1;
for (int i = 2; i <= 100000; i++)a[i] = (a[i - 1] * i)%mod;
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
LL k = (a[n] * n) % mod;
k = (k * (n - 1)) % mod;
cout << k << endl;
}
}
C. Quiz Master
题目大意:
这里有
n
n
n 个学生和
m
m
m 个种类的问题。
每个学生的知识储备用 a i a_i ai 来表示,对于第 T T T 个种类的问题,如果 a i % T = 0 a_i\%T=0 ai%T=0 那么第 i i i 个学生可以解决种类 T T T 的问题
现在给出这 n n n 个学生的知识储备 a i a_i ai ,现在需要给出,能解决所有种类问题的学生集合,并且使这个集合里的知识储备极差最小
1 ≤ n , m ≤ 1 e 5 1\le n,m\le 1e5 1≤n,m≤1e5
解答
- 首先预处理出数据范围内所有整数的所有因子
- 定义 c n t cnt cnt 数组,表示当前这个种类的题目有多少人可以做出来,并且维护它
- 把 a i a_i ai 按照从小到大排序(方便计算极差)
- 维护双指针(只要保证能遍历所有可能为最优的情况即可)
注:
本题对代码能力要求较高,代码逻辑多研究研究
单词积累:
proficient 熟练 rookie 新手 collectively 集体的 maximum difference 极差
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int N = 1e5+10;
vector<vector<int>> fac(N);
int a[N];
int cnt[N];
int tag, tot, n, m;
void upd(int num, int v) {
if (num > m)return;
if (v == 1) {
if (++cnt[num] == 1)tot++;
}
else {
if (--cnt[num] == 0)tot--;
}
tag = tot == m;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
for (int i = 1; i < N; i++)
for (int j = i; j < N; j += i)
fac[j].push_back(i);
int T;
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
sort(a, a + n);
memset(cnt, 0, sizeof cnt);
int ans = -1;
for (int i = 0, j = 0; i < n; i++) {
while (!tag && j < n) {
for (auto d : fac[a[j]])upd(d, 1);
j++;
}
if (tag) {
if (ans == -1 || ans > a[j - 1] - a[i]) {
ans = a[j - 1] - a[i];
}
}
for (auto d : fac[a[i]])upd(d, -1);
}
cout << ans << endl;
}
return 0;
}
D. Score of a Tree
题目大意:
给出一个结点个数为 n n n ,带点权的树,每个点的权值为 0 0 0 或者 1 1 1 ,每过一个时刻,每个结点的权值变成所有子树权值的异或。
设 S ( t ) S(t) S(t) 为时刻 t t t 的时候,树上所有结点的权值相加的值。
设 F ( A ) F(A) F(A) 为树 A A A 在整个时间轴上所有 S ( t ) S(t) S(t) 相加
现在需要求一个树所有 2 n 2^n 2n 种初始情况的所有 F ( A ) F(A) F(A) 相加,并且对 1 e 9 + 7 1e9+7 1e9+7 取模
解答:
我们发现,所有种类初始情况下,每个点出现的权值期望为 0.5 0.5 0.5 。所以单个点对答案的总贡献为 2 n × 1 2 2^n\times\frac{1}{2} 2n×21 ,再计算每个点的存活时间 T i T_i Ti ,则答案为 ∑ i = 1 n 2 n − 1 × T i \sum\limits_{i=1}^n 2^{n-1} \times T_i i=1∑n2n−1×Ti 。
- 首先把 2 2 2 的幂全部预处理出来,并且每次模上 m o d mod mod 。
- 将图读入。记得初始化 h h h 数组和 i d x idx idx (不能使用 m e m s e t ( ) memset() memset() ,否则会TLE)。
- d f s dfs dfs 一遍,计算出所有 T i T_i Ti 的和。
- 把两者相乘并取模,再输出。
注:
- 此处要存储无向边,否则方向无法保证
- 使用 m e m s e t memset memset会TLE
- 记得每次初始化 i d x idx idx 和 h h h 数组
- d f s dfs dfs的时候返回值和答案不要混淆,要记得邻接表存的话,终点是 e [ i ] e[i] e[i] 而不是 i i i
单词积累:
bitwise XOR 异或 configurations 布局
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 2e5 + 2, mod = 1e9 + 7;
int n;
int h[N], e[2 * N], ne[2 * N], idx;
LL res;
LL powt[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int dfs(int u, int fa) {
int height = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (fa == j)continue;
height = max(height, dfs(j, u) + 1);
}
res = (res + height) % mod;
return height;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
powt[0] = 1;
for (int i = 1; i < N; i++) {
powt[i] = 2 * powt[i - 1] % mod;
}
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 0; i <= n; i++)h[i] = -1;//使用memset会TLE
idx = 0;
for (int i = 0; i < n - 1; i++) {
int a, b;
cin >> a >> b;
add(a, b);
add(b, a);
}
res = 0;
dfs(1, 0);
cout << (res * powt[n - 1]) % mod << endl;
}
}