Codeforces Round #737 (Div. 2)

A

容易发现,若将数列中的最大值与其他元素分在一起,只会使其贡献减少。

由于只能分成 \(2\) 个非空子序列,我们将最大值划分到其中一个,剩下的元素划分到另一个即可。

代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
 
using namespace std;
 
typedef long long ll;
 
int a[100007];
 
int main(){
	int t;
	cin >> t;
	for (int i = 1; i <= t; i++){
		int n;
		ll sum = 0;
		cin >> n;
		for (int j = 1; j <= n; j++){
			cin >> a[j];
			sum += a[j];
		}
		sort(a + 1, a + n + 1);
		printf("%.8lf\n", a[n] + 1.0 * (sum - a[n]) / (n - 1));
	}
	return 0;
}

B

根据题目要求,可以发现离散化后值的大小相邻者如果不挨在一起,则需要新增一个子串

证明:加上它们不挨在一起时不需要新增子串,则将整个序列划分完后较大的值插不进来,会导致排序失败。

统计子串个数,若个数 \(\leq k\) 输出 Yes,否则输出 No 即可。

代码:

#include <iostream>
#include <algorithm>
 
using namespace std;
 
typedef long long ll;
 
int a[100007], b[100007], pos[100007];
 
int main(){
	int t;
	cin >> t;
	pos[0] = -1;
	for (int i = 1; i <= t; i++){
		int n, k, cnt = 0;
		cin >> n >> k;
		for (int j = 1; j <= n; j++){
			cin >> a[j];
			b[j] = a[j];
		}
		sort(b + 1, b + n + 1);
		for (int j = 1; j <= n; j++){
			a[j] = lower_bound(b + 1, b + n + 1, a[j]) - b;
			pos[a[j]] = j;
		}
		for (int j = 1; j <= n; j++){
			if (pos[j] != pos[j - 1] + 1) cnt++;
		}
		if (cnt <= k){
			cout << "Yes" << endl;
		} else {
			cout << "No" << endl;
		}
	}
	return 0;
}

C

做了这道题我才知道——我不会数数,我甚至不配数数。

显然,我们并不关心数组中每个数的实际值,我们只关心 \(\operatorname{and}\)\(\operatorname{xor}\) 运算后得到的值。设 \(\operatorname{and}\) 得到的值为 \(a\)\(\operatorname{xor}\) 得到的值为 \(b\)

\(dp_{i, j}\) 表示从第 \(0\) 位(这里指最高位)到第 \(i\) 位构造原数组的方法数。若 \(j = 0\),此时 \(a = b\);否则,此时 \(a > b\)

初始值:令 \(x = \displaystyle\sum_{i = 0}^{\lfloor \frac{n - 1}{2} \rfloor} C_n^{2i}\),则 \(dp_{0, 0} = x\)\(dp_{0, 1} = [n \bmod 2 = 0]\)

答案:\(dp_{k - 1, 0} + dp_{k - 1, 1}\)

转移:

  1. \(j = 0 \operatorname{and} n \bmod 2 = 0\)

显然必须要让这一位也相同,则 \(dp_{i, j} = x dp_{i - 1, 0}\)

  1. \(j = 0 \operatorname{and} n \bmod 2 = 1\)

显然必须要让这一位也相同,但由于全选也合法,则 \(dp_{i, j} = (x + 1) dp_{i - 1, 0}\)

  1. \(j = 1 \operatorname{and} n \bmod 2 = 0\)
  • 你可以让之前的位已经满足 \(a > b\),再随意安排这一位。
  • 你可以让之前的位已经满足 \(a = b\),再让这一位安排后满足 \(a > b\)

\(dp_{i, j} = 2^n dp_{i - 1, 1} + dp_{i - 1, 0}\)

  1. \(j = 1 \operatorname{and} n \bmod 2 = 1\)

手玩一下会发现此时没有合法情况,则 \(dp_{i, j} = 0\)

预处理组合数后直接转移即可。注意需要特判 \(k = 0\) 的情况。

代码:

#include <stdio.h>

typedef long long ll;

const int N = 2e5 + 7, M = 1 + 7, mod = 1e9 + 7;
ll fac[N], inv_fac[N], dp[N][M];

inline ll quick_pow(ll x, ll p, ll mod){
	ll ans = 1;
	while (p){
		if (p & 1) ans = ans * x % mod;
		x = x * x % mod;
		p >>= 1;
	}
	return ans;
}

inline void init(){
	fac[0] = 1;
	for (int i = 1; i < N; i++){
		fac[i] = fac[i - 1] * i % mod;
	}
	inv_fac[N - 1] = quick_pow(fac[N - 1], mod - 2, mod);
	for (int i = N - 2; i >= 0; i--){
		inv_fac[i] = inv_fac[i + 1] * (i + 1) % mod;
	}
}

inline ll comb(ll n, ll m, ll mod){
	if (m == 0) return 1;
	if (m > n) return 0;
	return fac[n] * inv_fac[m] % mod * inv_fac[n - m] % mod;
}

int main(){
	int t;
	scanf("%d", &t);
	init();
	for (int i = 1; i <= t; i++){
		int n, k;
		scanf("%d %d", &n, &k);
		if (k == 0){
			printf("1\n");
			continue;
		}
		ll x = 0, ans;
		for (int j = 0; j < n; j += 2){
			x = (x + comb(n, j, mod)) % mod;
		}
		if (n % 2 == 0){
			ll y = quick_pow(2, n, mod);
			dp[0][0] = x;
			dp[0][1] = 1;
			for (int j = 1; j < k; j++){
				dp[j][0] = dp[j - 1][0] * x % mod;
				dp[j][1] = (dp[j - 1][1] * y % mod + dp[j - 1][0]) % mod;
			}
		} else {
			dp[0][0] = x + 1;
			dp[0][1] = 0;
			for (int j = 1; j < k; j++){
				dp[j][0] = dp[j - 1][0] * (x + 1) % mod;
				dp[j][1] = 0;
			}
		}
		printf("%lld\n", (dp[k - 1][0] + dp[k - 1][1]) % mod);
	}
	return 0;
}
posted @ 2021-08-10 10:49  LovelyLeasier  阅读(66)  评论(0)    收藏  举报