Codeforces Round #735 (Div. 2) 题解(A-D)
A. Cherry
题目大意:
有一个长度为 n n n的整数数组 a a a。找出 m a x ( a l , a l + 1 , . . . , a r ) × m i n ( a l , a l + 1 , . . . a r ) , 1 ≤ l < r ≤ n max(a_l,a_{l+1},...,a_r)\times min(a_l,a_{l+1},...a_{r}),1\le l <r \le n max(al,al+1,...,ar)×min(al,al+1,...ar),1≤l<r≤n的最大值。
解题思路:
通过观察样例,我们可以大胆地猜测最优解的区间长度不会超过 2 2 2,所以我们只用遍历一遍数组即可,时间复杂度 O ( n ) O(n) O(n)。
现在简单论证一下为什么这种做法是正确的:
如果存在一个区间 [ l , l + 2 ] [l,l+2] [l,l+2]是优于 [ l , l + 1 ] [l,l+1] [l,l+1]和 [ l + 1 , l + 2 ] [l+1,l+2] [l+1,l+2]的,那么区间 [ l , l + 1 ] [l,l+1] [l,l+1]的最大值和最小值一定是 a l a_l al和 a l + 2 a_{l+2} al+2。
如果 a l a_l al是最大值的话,因为 a l + 1 ≥ a l + 2 a_{l+1}\ge a_{l+2} al+1≥al+2,那么一定存在 a l × a l + 1 ≥ a l × a l + 2 a_l\times a_{l+1}\ge a_l\times a_{l+2} al×al+1≥al×al+2。
同理,如果 a l + 2 a_{l+2} al+2是最大值的话,那么一定存在 a l + 2 × a l + 1 ≥ a l × a l + 2 a_{l+2}\times a_{l+1}\ge a_l\times a_{l+2} al+2×al+1≥al×al+2。
所以对于区间长度大于 2 2 2的区间,我们一定能找出不弱于当前区间的区间长度为 2 2 2的区间。
代码:
#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]);
LL res=0;
for(int i=1;i<n;i++){
res=max(res,(LL)a[i]*a[i+1]);
}
printf("%lld\n",res);
}
return 0;
}
B. Cobb
题目大意:
对于一个长度为 n n n的整数数组 a a a。找出 ∀ 1 ≤ i < j ≤ n , i × j − k × ( a i ∣ a j ) \forall \;1\le i<j\le n,i\times j-k\times(a_i|a_j) ∀1≤i<j≤n,i×j−k×(ai∣aj)的最大值,其中 ∣ | ∣表示位运算中的或运算。
数据范围: 1 ≤ n ≤ 1 0 5 , 1 ≤ k ≤ min ( n , 100 ) , 0 ≤ a i ≤ n 1\le n\le10^5,1\le k \le \min(n,100),0\le a_i\le n 1≤n≤105,1≤k≤min(n,100),0≤ai≤n.
解题思路:
由于 n n n数据范围的限制,如果本题用 O ( n 2 ) O(n^2) O(n2)的暴力做法是会 T L E TLE TLE的。
通过观察 i × j − k × ( a i ∣ a j ) i\times j-k\times(a_i|a_j) i×j−k×(ai∣aj)这个结构,我们发现对于前半部分的 i × j i\times j i×j,我们一定是会在 ( n − 1 , n ) (n-1,n) (n−1,n)这一对取到最大值,又因为 0 ≤ a n − 1 ∣ a n ≤ 2 × n 0\le a_{n-1}|a_n\le 2\times n 0≤an−1∣an≤2×n, ( n − 1 , n ) (n-1,n) (n−1,n)这一对的最小值是 min ( n − 1 , n ) = ( n − 1 ) × n − k × 2 × n = n 2 − 2 × k × n − n \min(n-1,n)=(n-1)\times n-k\times2\times n=n^2-2\times k\times n-n min(n−1,n)=(n−1)×n−k×2×n=n2−2×k×n−n。
那么我们考虑对于任何一对 ( i , j ) (i,j) (i,j),其中 i < j i<j i<j。对于包含 i i i的某对而言,能取得的最大值就是 j = n j=n j=n并且 a i ∣ a j = 0 a_i|aj=0 ai∣aj=0 的时候,此时能取得的最大值 max ( i , j ) = i × n \max(i,j)=i\times n max(i,j)=i×n。
对于上述得到的信息,我们可以选择将那些没有希望大于 m i n ( n − 1 , n ) min(n-1,n) min(n−1,n)的pair过滤掉,
即 m a x ( i , j ) ≤ m i n ( n − 1 , n ) ⇒ i × n ≤ n 2 − 2 × k × n − n ⇒ i < = n − 2 × k − 1 max(i,j)\le min(n-1,n) \Rightarrow i\times n\le n^2 - 2\times k \times n -n \Rightarrow i<=n-2\times k-1 max(i,j)≤min(n−1,n)⇒i×n≤n2−2×k×n−n⇒i<=n−2×k−1,
所以我们只用考虑 i ≥ n − 2 ∗ k i\ge n-2*k i≥n−2∗k这一部分pair就行,时间复杂度 O ( k 2 ) O(k^2) O(k2)。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e5+10;
int a[N];
int n,k;
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
LL res=-1e9;
for(int i=max(1,n-2*k);i<=n;i++)
for(int j=i+1;j<=n;j++)
res=max(res,(LL)i*j-k*(a[i]|a[j]));
printf("%lld\n",res);
}
return 0;
}
C. Mikasa
题目大意:
给定整数 n n n和 m m m,找出序列 n ⊕ 0 , n ⊕ 1 , . . . , n ⊕ m n\oplus 0,n\oplus1,...,n\oplus m n⊕0,n⊕1,...,n⊕m中未出现的最小非负整数,其中 ⊕ \oplus ⊕是位运算中的异或运算。
解题思路:
转换一下思路,可以将原问题转换为找一个最小的整数k使得 n ⊕ k > m n\oplus k>m n⊕k>m。
设 p = m + 1 p=m+1 p=m+1,在使得k尽可能小的情况下,我们从高位向低位考虑以下四种情况,其中 n i , p i , k i n_i,p_i,k_i ni,pi,ki分别代表 n , p , k n,p,k n,p,k二进制表示下的第 i i i位:
- n i = 0 , p i = 0 , 那 么 k i = 0 n_i=0,p_i=0,那么k_i=0 ni=0,pi=0,那么ki=0。
- n i = 0 , p i = 1 , 那 么 k i = 1 n_i=0,p_i=1,那么k_i=1 ni=0,pi=1,那么ki=1。
- n i = 1 , p i = 0 , 那 么 k i = 0 n_i=1,p_i=0,那么k_i=0 ni=1,pi=0,那么ki=0。
- n i = 1 , p i = 1 , 那 么 k i = 0 n_i=1,p_i=1,那么k_i=0 ni=1,pi=1,那么ki=0。
我们发现只有当 n i = 0 , p i = 1 n_i=0,p_i=1 ni=0,pi=1的时候,我们才需要将 k i k_i ki变为1。并且为了使得k尽可能小,一但满足 n ⊕ k ≥ p n\oplus k\ge p n⊕k≥p就要停止循环。
所以只需要从高位向低位枚举 n n n和 m m m的所有二进制位,时间复杂度 O ( l o g 2 n ) O(log_2n) O(log2n)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
m++;
int res=0;
for(int i=30;i>=0&&n<m;i--){
if(!(n>>i&1)&&m>>i&1) n|=1<<i,res|=1<<i;
}
printf("%d\n",res);
}
return 0;
}
D. Diane
题目大意:
给定一个整数 n n n。构造出一个长度为 n n n由小写字母组成的字符串,其中对于字符串中的任何一个子串在字符串中出现奇数次。
解题思路:
对于连续的 k k k个 a a a,会产生 k k k个"a", k − 1 k-1 k−1个"aa", k − 2 k-2 k−2个"aaa"…,每种字符串的数量是奇偶交替的。
那么我们可以选择再添加连续的 k − 1 k-1 k−1个 a a a,这一串连续的会产生 k − 1 k-1 k−1个"a", k − 2 k-2 k−2个"aa"…,刚好将上面连续的 k k k个 a a a产生的偶数次字符串修正为奇数次。
当然这两个字符串中间需要加字母来隔开,我们发现 k + k − 1 k+k-1 k+k−1永远是一个奇数,那么如果 n n n是奇数的话,我们就需要在中间添上两个字母作为挡板, n n n是偶数的话就只用添上一个字母。
所以我们对于 n n n是奇数的情况,可以构造出 a a a . . . a a a ⏟ k b c a a a . . . a a a ⏟ k − 1 \underbrace{aaa...aaa}_{k} bc\underbrace{aaa...aaa}_{k-1} k aaa...aaabck−1 aaa...aaa,当n=1的时候需要特判一下。
n n n是偶数的情况,可以构造出 a a a . . . a a a ⏟ k b a a a . . . a a a ⏟ k − 1 \underbrace{aaa...aaa}_{k} b\underbrace{aaa...aaa}_{k-1} k aaa...aaabk−1 aaa...aaa。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
if(n==1){
puts("a");
continue;
}
for(int i=0;i<n/2-1;i++) printf("a");
printf(n%2?"bc":"b");
for(int i=0;i<n/2;i++) printf("a");
puts("");
}
return 0;
}