Codeforces Round #737 (Div. 2) 题解(A-C)
A. Ezzat and Two Subsequences
题目大意:
给定 n n n个整数,将这 n n n个整数分成两组,使得两组整数的平均数之和最大,输出这个最大的平均数之和。
解题思路:
通过观察样例可以发现,只要将最大的整数单独作为一组就行了。
那么为什么这样是对的呢,可以考虑进行这样分组之和,从 n − 1 n-1 n−1个整数那组拿任意一个整数过来能否取得更优的解,列个式子推一推就能明白了,因为本人比较懒就不写详细的证明过程了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int a[N];
int n;
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);
double sum=0;
for(int i=1;i<n;i++) sum+=a[i];
sum/=(n-1);
printf("%.8f\n",sum+a[n]);
}
return 0;
}
B. Moamen and k-subarrays
题目大意:
给出 n n n个不同的整数,问是否能将这 n n n个整数分成 k k k块连续的子数组之后,通过移位组合成一个升序的数组。
解题思路:
因为分块过后,子数组内部的顺序是不会发生改变的,而我们最后要得到的数组一定是按照严格的顺序排好的。
所以只要我们将整个数组按照升序排序,得到每个数组在升序数组中的位置。
设 a i a_i ai在最终数组的下标是 b i b_i bi,那么当且仅当 b i = b i − 1 + 1 b_i=b_{i-1}+1 bi=bi−1+1的情况下 a i 和 a i − 1 a_i和a_{i-1} ai和ai−1才能分到同一块中。
相对的,如果 b i ≠ b i − 1 + 1 b_i\neq b_{i-1}+1 bi=bi−1+1,那么 b i b_i bi和 b i − 1 b_{i-1} bi−1一定要被分成两块。
要注意的一点是分成一块是没有意义的,所以我们要将答案初始化为 1 1 1。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a[N],b[N];
int n,k;
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
vector<int> v;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
for(int i=0;i<n;i++) b[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin();
int res=1;
for(int i=1;i<n;i++){
if(b[i]!=b[i-1]+1) res++;
}
puts(k>=res?"YES":"NO");
}
return 0;
}
C. Moamen and XOR
题目大意:
构造一个 n n n个整数的数组 a a a,其中每个整数都要小于 2 k 2^k 2k。
问有多少种方案能够使得以下条件成立:
a 1 & a 2 & a 3 & . . . & a n ≥ a 1 ⊕ a 2 ⊕ a 3 ⊕ . . . ⊕ a n a_1\And a_2 \And a_3\And...\And a_n\ge a_1\oplus a_2 \oplus a_3\oplus ...\oplus a_n a1&a2&a3&...&an≥a1⊕a2⊕a3⊕...⊕an
解题思路:
做法有点像数位dp,但其实也可以说是二进制的线性dp。
因为所有整数要小于 2 k 2^k 2k,所以对于每个整数在二进制的表示下最多只有 k k k位(如果 k = 0 k=0 k=0的话,那所有数都等于1,答案也就是1)。
所以我们从高位向低位考虑,考虑每一位的所有情况:
- 如果当前位 & \And &和 ⊕ \oplus ⊕的结果分别是 1 1 1和 1 1 1,那么只有所有数这一位都为 1 1 1,并且n是奇数,一共只有 1 1 1种情况。
- 如果当前位 & \And &和 ⊕ \oplus ⊕的结果分别是 1 1 1和 0 0 0,那么只有所有数这一位都为1,并且n是偶数,一共只有 1 1 1种情况。
- 如果当前位
&
\And
&和
⊕
\oplus
⊕的结果分别是
0
0
0和
1
1
1,那么只有奇数个数的这一位为1,并且不能全部的数这一位都为1。
方案数: { 2 n − 1 − 1 n % 2 = 1 2 n − 1 n % 2 = 0 \begin{cases}2^{n-1}-1&n\%2=1 \\ 2^{n-1} \ &n\%2=0\end{cases} {2n−1−12n−1 n%2=1n%2=0 - 如果当前位
&
\And
&和
⊕
\oplus
⊕的结果分别是
0
0
0和
0
0
0,那么只有偶数个数的这一位为1,并且不能全部的数这一位都为1。
方案数: { 2 n − 1 n % 2 = 1 2 n − 1 − 1 n % 2 = 0 \begin{cases}2^{n-1}&n\%2=1 \\ 2^{n-1}-1 \ &n\%2=0\end{cases} {2n−12n−1−1 n%2=1n%2=0
可能会有人不知道 2 n − 1 2^{n-1} 2n−1怎么来的,众所周知组合数的和 ∑ n = 0 m C m n = 2 m \sum\limits_{n=0}^m C_m^n=2^m n=0∑mCmn=2m,其中奇数项和偶数项的和都等于 2 m − 1 2^{m-1} 2m−1。
设 f [ i ] [ 0 ] f[i][0] f[i][0]为仅考虑最高的 i i i位使得 & \And &和 ⊕ \oplus ⊕的结果相等的方案数, f [ i ] [ 1 ] f[i][1] f[i][1]为仅考虑最高的 i i i位使得 & \And &的结果大于 ⊕ \oplus ⊕的方案数。
那么可以通过上述分析得到状态转移方程:
- f [ i ] [ 0 ] = { f [ i − 1 ] [ 0 ] × ( 2 n − 1 + 1 ) n % 2 = 1 f [ i − 1 ] [ 0 ] × ( 2 n − 1 − 1 ) n % 2 = 0 f[i][0]=\begin{cases}f[i-1][0]\times(2^{n-1}+1)&n\%2=1\\f[i-1][0]\times(2^{n-1}-1) &n\%2=0\end{cases} f[i][0]={f[i−1][0]×(2n−1+1)f[i−1][0]×(2n−1−1)n%2=1n%2=0(就是在前一位相等的基础上分别添上00和11, & \And &和 ⊕ \oplus ⊕仍然相等)
-
f
[
i
]
[
1
]
=
{
f
[
i
−
1
]
[
1
]
×
2
n
n
%
2
=
1
f
[
i
−
1
]
[
0
]
+
f
[
i
−
1
]
[
1
]
×
2
n
n
%
2
=
0
f[i][1]=\begin{cases}f[i-1][1]\times2^n&n\%2=1\\ f[i-1][0]+f[i-1][1]\times2^n&n\%2=0\end{cases}
f[i][1]={f[i−1][1]×2nf[i−1][0]+f[i−1][1]×2nn%2=1n%2=0
(如果在上一位已经使得 & \And &大于 ⊕ \oplus ⊕,那么在当前位对于任何一种情况都是 & \And &大于 ⊕ \oplus ⊕。如果 n n n是偶数的话,会有一种所有整数在当前二进制位都为1使得 & \And &和 ⊕ \oplus ⊕的结果是1和 0 0 0的情况,这种可以使得上一位的相等状态转移到当前的大于状态)
边界为 f [ 0 ] [ 0 ] = 1 f[0][0]=1 f[0][0]=1。
计算 2 n 2^n 2n用快速幂就行了,在容易溢出的地方别忘了取模。
时间复杂度 O ( l o g n + k ) O(logn+k) O(logn+k)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+10,mod=1e9+7;
int n,k;
LL f[N][2];
int qmi(int a,int b){
int res=1;
while(b){
if(b&1) res=(LL)res*a%mod;
a=(LL)a*a%mod;
b>>=1;
}
return res;
}
int main()
{
f[0][0]=1;
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
if(!k){
puts("1");
}
else{
LL p=qmi(2,n-1);
for(int i=1;i<=k;i++){
if(n%2){
f[i][0]=(f[i-1][0]*(p+1))%mod;
f[i][1]=(f[i-1][1]*p*2%mod)%mod;
}
else{
f[i][0]=(f[i-1][0]*(p-1))%mod;
f[i][1]=(f[i-1][0]+f[i-1][1]*p*2)%mod;
}
}
printf("%d\n",(f[k][0]+f[k][1])%mod);
}
}
return 0;
}