Codeforces Round #845(div2)A~D题解

Codeforces Round #845(div2)

A. Everybody Likes Good Arrays!

题目大意:

长度为 n n n 的正整数数组 A A A 若是满足相邻两个元素奇偶性不同,则称为好数组。

每次操作可以把 A A A 数组任意两个相邻的元素删除,并且在原位置插入它们的乘积。

问最少需要几次操作。

n ≤ 100 n\le100 n100

解答:

我们需要知道:

  • 奇数与奇数相乘还是奇数,偶数与偶数相乘还是偶数

所以,如果数组里面有连续的 n n n 个相同奇偶性的元素,就需要 n − 1 n-1 n1 次合并。扫描一次数组,找到所有连续的相同的奇偶性的元素,并且在答案上加上 n − 1 n-1 n1 即可。

小技巧:

由于我们只考虑奇偶性,所以我们读入数组的时候只需要读入奇偶性即可,即设置数组为 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 n1e5

解答:

我们需要知道:

  • 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) Cn22=n(n1) 个,那么所有的排列的逆序对就是 n ! ∗ n ( n − 1 ) n!*n(n-1) n!n(n1) 个。

单词积累:

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 1n,m1e5

解答

  1. 首先预处理出数据范围内所有整数的所有因子
  2. 定义 c n t cnt cnt 数组,表示当前这个种类的题目有多少人可以做出来,并且维护它
  3. a i a_i ai 按照从小到大排序(方便计算极差)
  4. 维护双指针(只要保证能遍历所有可能为最优的情况即可)

注:

本题对代码能力要求较高,代码逻辑多研究研究

单词积累:

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=1n2n1×Ti

  1. 首先把 2 2 2 的幂全部预处理出来,并且每次模上 m o d mod mod
  2. 将图读入。记得初始化 h h h 数组和 i d x idx idx (不能使用 m e m s e t ( ) memset() memset() ,否则会TLE)。
  3. d f s dfs dfs 一遍,计算出所有 T i T_i Ti 的和。
  4. 把两者相乘并取模,再输出。

注:

  1. 此处要存储无向边,否则方向无法保证
  2. 使用 m e m s e t memset memset会TLE
  3. 记得每次初始化 i d x idx idx h h h 数组
  4. 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;

	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiutianhan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值