1. 程式人生 > 其它 >Codeforces Round #737 (Div. 2)

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\)

初始值:\(dp_{-1, 0} = dp_{-1, 1} = 1\)

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

轉移:

  1. \(j = 0\)

顯然 \(dp_{i, j} = 2^n dp_{i - 1, j}\)

  1. \(j = 1 \operatorname{and} n \bmod 2 = 0\)
  • 你可以讓這位為 \(1\) 的元素數量為偶數(當然,不能為 \(n\)),此時 \(a = b\)
  • 你可以讓這位為 \(1\) 的元素數量為 \(n\),此時 \(a > b\)

\(x = \displaystyle\sum_{i = 0}^{\lfloor \frac{n - 1}{2} \rfloor} C_n^{2i}\),則 \(dp_{i, j} = x dp_{i - 1, 1} + dp_{i - 1, 0}\)

  1. \(j = 1 \operatorname{and} n \bmod 2 \neq 0\)
  • 你可以讓這位為 \(1\) 的元素數量為偶數,此時 \(a = b\)
  • 你可以讓這位為 \(1\) 的元素數量為 \(n\),此時仍然 \(a = b\)

\(dp_{i, j} = (x + 1) dp_{i - 1, 1}\)

預處理組合數後直接轉移即可。注意需要特判 \(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 = dp[0][0] = quick_pow(2, n, mod);
			dp[0][1] = x + 1;
			for (int j = 1; j < k; j++){
				int jd = j - 1;
				dp[j][0] = y * dp[jd][0] % mod;
				dp[j][1] = (x * dp[jd][1] % mod + dp[jd][0]) % mod;
			}
			ans = dp[k - 1][1];
		} else {
			ans = quick_pow(x + 1, k, mod);
		}
		printf("%lld\n", ans);
	}
	return 0;
}