Educational Codeforces Round 111 (Rated for Div. 2) 题解(A-C)
A. Find The Array
题目大意:
如果一个由 n n n个正整数组成的数组 a a a中每一位 a i ( 1 ≤ i ≤ n ) a_i(1\le i \le n) ai(1≤i≤n)满足以下条件:
- a i = 1 a_i=1 ai=1
- a i − 1 a_i-1 ai−1或者 a i − 2 a_i-2 ai−2也存在于这个数组中
我们就称这个数组是漂亮的。
现在给出一个正整数 s s s,在保证漂亮数组和等于 s s s的情况下,数组的长度最小是多少。
解题思路:
很容易想到,在和固定的情况下,我们要使得数组中元素的个数最小,就得使得每个元素尽可能大,由于题目条件的限制,我们很容易想到 1 , 3 , 5 , 7 , . . . , 2 × n − 1 1,3,5,7,...,2\times n -1 1,3,5,7,...,2×n−1这种构造方式。同时我们可以发现,如果按照这种方式构造数组之和超过了s,我们可以将部分数字减去 1 1 1或 2 2 2,因为我们最多可以减去 2 × ( n − 1 ) 2\times(n-1) 2×(n−1),所以但凡是用 n − 1 n-1 n−1个数字之和无法大于等于 s s s的情况下,我们都可以用 n n n个数字完成构造。
代码:
#include<cstdio>
int n;
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int sum=0,res=0;
for(int i=1;sum<n;i+=2){
sum+=i;
res++;
}
printf("%d\n",res);
}
return 0;
}
B. Maximum Cost Deletion
题目大意:
给出一个01字符串,每次可以删除一段相同的子串,假设每次删除的长度是 l l l,我们可以获得 a × l + b a\times l+b a×l+b的分数,问把字符串删成空串的情况下最高的分数是多少。
解题思路:
首先,我们可以发现,设字符串长度为 n n n,分数一定是 a × n + k × b a\times n+k\times b a×n+k×b,其中 k k k是执行操作的次数。
因为 − 100 ≤ b ≤ 100 -100\le b \le 100 −100≤b≤100,所以我们可以根据 b b b的正负来选择贪心策略。
- 如果 b > = 0 b>=0 b>=0,很显然我们需要尽可能多地执行操作,这时可以每次删除一个字符,一共删除 n n n次,所以答案就是 ( a + b ) ∗ n (a+b)*n (a+b)∗n。
- 如果b<0,很显然我们需要尽可能少地执行操作,这时我们可以每次删除一段连续的 0 0 0串或者 1 1 1串,最后再一次删除剩余的字符串,分别计算一下两种策略的次数取个min就行了。
代码:
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
string s;
int n,a,b;
int main()
{
int T;
cin>>T;
while(T--){
cin>>n>>a>>b;
cin>>s;
int res=a*n;
if(b>=0) res+=b*n;
else{
int num0=0,num1=0;
for(int i=0;i<s.size();){
if(s[i]=='0') num0++;
else num1++;
int j=i+1;
while(j<s.size()&&s[j]==s[i]) j++;
i=j;
}
res+=b*min(num0+1,num1+1);
}
cout<<res<<endl;
}
return 0;
}
C. Manhattan Subarrays
题目大意:
对于两个点 p = ( x p , y p ) p=(x_p,y_p) p=(xp,yp)和 q = ( x q , y q ) q=(x_q,y_q) q=(xq,yq),我们定义这两个点的曼哈顿距离 d ( p , q ) = ∣ x p − x q ∣ + ∣ y p + y q ∣ d(p,q)=|x_p-x_q|+|y_p+y_q| d(p,q)=∣xp−xq∣+∣yp+yq∣。
如果点 p , q , r p,q,r p,q,r满足 d ( p , r ) = d ( p , q ) + d ( q , r ) d(p,r)=d(p,q)+d(q,r) d(p,r)=d(p,q)+d(q,r),我们就称这三个点是一个坏三元组。
如果对于一个数组 b 1 , b 2 , . . . , b m b_1,b_2,...,b_m b1,b2,...,bm,如果我们无法选择三个不同的整数 i , j , k ( 1 ≤ i , j , k ≤ m ) i,j,k(1\le i,j,k\le m) i,j,k(1≤i,j,k≤m)使得点 ( b i , i ) , ( b j , j ) , ( b k , k ) (b_i,i),(b_j,j),(b_k,k) (bi,i),(bj,j),(bk,k)构成坏三元组的话,我们就称这个数组是好的。
现在给出一个数组 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,请计算 a a a中有多少个子数组是好的。
如果某个数组的长度小于 3 3 3,那么这个数组也是好数组。
解题思路:
我们可以发现,如果对于某三元组 ( i , j , k ) (i,j,k) (i,j,k),其中 1 ≤ i < j < k ≤ n 1\le i<j<k\le n 1≤i<j<k≤n,如果 a i , a j , a k a_i,a_j,a_k ai,aj,ak是不升或者不降的序列,那么这三个点就能构造坏三元组。
所以问题就转化成为了,找出有多少个子数组中不存在非升或非降的三元组。
再仔细观察一下,可以发觉对于任何一个好数组,最大的长度是4,一但长度大于4,就一定会出现一个坏三元组。
所以我们可以通过从左向右遍历整个数组,枚举一下从当前位最多向前延伸多长,这个长度即为以当前元素作为最后子数组结尾的个数,累加起来即可。
看似有多层循环,但因为好数组长度的限制,所以在向前延伸的循环并不会执行太多次,时间复杂度还是线性的。
代码:
#include<cstdio>
const int N=2e5+10;
int a[N];
int n;
bool check(int l,int r){
if(r-l+1<=2) return true;
for(int i=l;i+2<=r;i++)
for(int j=i+1;j+1<=r;j++)
for(int k=j+1;k<=r;k++)
if((long long)(a[i]-a[j])*(a[j]-a[k])>=0) return false;
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int res=0;
for(int i=1;i<=n;i++){
int j=i;
bool flag=true;
while(j>=1){
if(!check(j,i)) break;
j--;
}
res+=(i-j);
}
printf("%d\n",res);
}
return 0;
}