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}\)。
转移:
- 当 \(j = 0 \operatorname{and} n \bmod 2 = 0\)
显然必须要让这一位也相同,则 \(dp_{i, j} = x dp_{i - 1, 0}\)。
- 当 \(j = 0 \operatorname{and} n \bmod 2 = 1\)
显然必须要让这一位也相同,但由于全选也合法,则 \(dp_{i, j} = (x + 1) dp_{i - 1, 0}\)。
- 当 \(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}\)。
- 当 \(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;
}

浙公网安备 33010602011771号