Codeforces Round #767 (Div. 2) A~D题解
A.Download More RAM
题意:
T组数据:
给n组a和b,给一个初始值k。
如果a[i]<=k,那么k加上b[i]
输出k的最大值
思路:
用a当关键字从小到大排序,一个for循环跑一遍一直累加b,知道k<a[i]为止
时间复杂度:\(O(n)\)
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
#define int long long
int T,n,k;
struct yzh
{
int a,b;
}cf[10010];
bool cmp(yzh a,yzh b){
return a.a<b.a;
}
signed main(){
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;++i){
scanf("%lld",&cf[i].a);
}
for(int i=1;i<=n;++i){
scanf("%lld",&cf[i].b);
}
sort(cf+1,cf+n+1,cmp);
for(int i=1;i<=n;++i){
if(k>=cf[i].a){
k+=cf[i].b;
}
else{
break;
}
}
printf("%lld\n",k);
}
return 0;
}
B.GCD Arrays
题意:
T组数据,每组数据给l,r,k。
定义数组a的元素为l~r的整数,要求k次删除操作使得a中所有元素的最大公约数>1。
思路:
思考怎么删除是最优的,我们发现a中的元素一定是连续的整数,若让最大公约数>1的话只留下偶数(即最大公约数为2)是最优解。
这是因为偶数相隔一个数,且GCD一定大于等于2,而要求更高的GCD间隔一定大于一。所以去除奇数是最少方案.
复杂度:\(O(1)\)
注:数据中如果有l=r且l>1那么直接输出YES
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
#define int long long
int T,l,r,k;
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&l,&r,&k);
int t=(r-l+1)/2;
if(r%2==1&&t*2!=(r-l+1)) ++t;//若l到r正整数个数为奇数,那么就要进行特判
if(r==l&&r>1){
puts("YES");
continue;
}
if(t>k){
puts("NO");
}
else{
puts("YES");
}
}
return 0;
}
C.Meximum Array
题意:
给定一个由n个非负整数组成的数组a,Mihai希望创建一个新的数组b,该数组的形成方式如下:
当a不为空时有一下操作:
选择一个整数k(1≤K≤|a|)。
将数组a的前k个数字的MEX附加到数组b的末尾,并从数组a中删除它们,从而移动a中其余数字的位置。
要求输出字典序最大的b串。
其中,一组非负整数的MEX是不在该集合的非负整数的最小值。例如,MEX({1,2,3}) =0 and MEX({0,1,2,4,5}) =3。
思路:
建一个数组vis记录每个数出现的次数。注:这个vis在代码中定义的是b,题目中的b数组在题目中定义的为ans
然后对vis下标从0到往后找,知道找到vis[t]为0时,这就是b数组的第一个数(因为t为最小出现次数为0的数,所以t是生成MEX的极限,不可能有比t更大的MEX)。
然后取k,要求尽量k尽量小,我们就定义vist数组用来标记删除点,一个for循环进行删除前k个值的操作,若小于t的vis都已经进行过删除标记,那么就停止删除,这样就可以达到k的尽量小。
至于复杂度,各位也能发现这是一个NP问题,是没有多项式复杂度的,但是也能过,如果哪位大佬能用P来求解,那么欢迎留言。
代码:
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int t,n,a[200010],b[200010],m,ans[200010];
bool vis[200010];
signed main(){
scanf("%d",&t);
while(t--){
memset(b,0,sizeof(b));
m=0;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
b[a[i]]++;
}
int id=0;
while(id<n){
int t=0;
while(b[t]){
++t;
}
// cout<<"::"<<t<<endl;
if(!t){
ans[++m]=0;
id++;
continue;
}
else{
ans[++m]=t;
}
int it=t;
memset(vis,false,sizeof(vis));
while(it){
++id;
if(!vis[a[id]]&&a[id]<t){
it--;
vis[a[id]]=true;
}
b[a[id]]--;
}
}
printf("%d\n",m);
for(int i=1;i<=m;++i){
printf("%d ",ans[i]);
}
puts("");
}
return 0;
}
D.Peculiar Movie Preferences
题意:
有n个字符串(长度不超过3),要求删除若干个字符串(可以为0),剩下的串按照顺序拼起来,能否组成一个回文串。
思路:
如果一个串自身就是一个回文那么直接输出YES
然后就是分多种情况讨论:
(AB)(BA)
(ABC)(BA)
(AB)(CBA)
(ABC)(CBA)
这几种情况进行判断即可。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<set>
using namespace std;
int T,n;
string s[100010];
bool flag[100010];
set<string> st;
signed main(){
scanf("%d",&T);
while(T--){
st.clear();
memset(flag,false,sizeof(flag));
scanf("%d",&n);
int flt=false;
for(int i=1;i<=n;++i){
cin>>s[i];
int len=s[i].length();
if(s[i][0]==s[i][len-1]&&s[i][1]==s[i][len-2]){//自身为回文
flag[i]=true;
flt=true;
}
if(len==2){
string t = "";
t = t + s[i][1] ;
t = t + s[i][0];
if (st.count(t)) flt = true;
}
if(len==3){
string t = "";
t = t + s[i][2];
t = t + s[i][1];
if (st.count(t)) flt = true;
t = t + s[i][0];
if (st.count(t)) flt = true;
}
st.insert(s[i]);
}
st.clear();
for(int i=n;i>=1;--i){//倒着找
int len=s[i].length();
if(len==3){
string t = "";
t = t+s[i][1];
t = t+s[i][0];
if (st.count(t)) {
flt = true;
break;
}
}
st.insert(s[i]);
}
if(flt){
puts("YES");
}
else{
puts("NO");
}
}
return 0;
}