目录
A:Number Replacement
一个简单的签到题
观察到一个数字不可能同时担任两个值的表示
故判断每一个数字是否担任两个角色即可
用map来使数字和字符产生映射,若不存在键值压入,若存在则判断相同与否
#include<bits/stdc++.h>
using namespace std;
int main(){
cin.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
bool flag=false;
vector<int>v;
for(int i=0;i<n;i++){
int k;
cin>>k;
v.push_back(k);
}
string s;
cin>>s;
map<int,char>mp;
for(int i=0;i<n;i++){
if(!mp.count(v[i])){
mp[v[i]]=s[i];
}else{
if(mp.count(v[i])){
if(mp[v[i]]==s[i]) continue;
else flag=true;
}
}
}
if(flag) puts("NO");
else puts("YES");
}
return 0;
}
B:Even-Odd Increments
奇数+奇数=偶数,奇数+偶数=奇数,偶数+偶数=偶数
观察到偶数不改变奇偶性,且一旦出现奇数+偶数或者奇数+奇数,数组中存在且仅存在一种数字,奇数偶数只能二选一
#include<bits/stdc++.h>
using namespace std;
int main(){
cin.tie(0);
int t;
cin>>t;
while(t--){
int n,q;
cin>>n>>q;
//离散化?不太需要
vector<long long>odd;
vector<long long>even;
long long sum=0;
for(int i=0;i<n;i++){
long long k;
cin>>k;
k&1?odd.push_back(k):even.push_back(k);
sum+=k;
}
int size=even.size();
for(int i=0;i<q;i++){
int choose,much;
cin>>choose>>much;
//奇奇偶,奇偶奇,偶偶偶
//得知偶数不影响,而奇数特判
//总数为n,得知偶数为size,则奇数必为n-size
if(!choose){
//给偶数相加
if(much&1){
//加的是奇数
sum+=much*size;
size=0;
cout<<sum<<'\n';
}else{
//加的是偶数
sum+=much*size;
cout<<sum<<'\n';
}
}else{
//给奇数相加
if(much&1){
//加的是奇数
sum+=(n-size)*much;
size=n;
cout<<sum<<'\n';
}else{
//加的是偶数
sum+=(n-size)*much;
cout<<sum<<'\n';
}
}
}
}
return 0;
}
C:Traffic Light
既然会循环
何不直接令s=s+s
使得不需要考虑再倒回去,只要判断到达原长度即可
主要是字符串类函数的应用
若从头遍历,会超时
如果从某处找到了op[0]
其率先找到的g(green,绿灯),这之间的所有东西是不需要判断的
因为其必定小于此时的max值mx
故实际上i=j+1即可
但是如果是多个g,那么又需要判断一群不为op[0]的值
故将i每次置为max(find(op[0],i+1),j+1)
实际上这个应该是对的,而当时由于脑抽,没多思考,直接写了这个,最后的置换中,max的前半部分是没用的。(注释掉的那一行)
这样子的话可以从390ms的运行时间提升到46ms的运行时间。
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin.tie(0);
cin>>t;
cout.tie(0);
while(t--){
int n;
char op[2];
cin>>n>>op;
string s;
cin>>s;
s=s+s;
int mx=0;
if(op[0]=='g') cout<<mx<<'\n';
else{
for(int i=0;i<n;){
i=s.find(op[0],i);
if(i==-1||i>=n) break;
int j=s.find('g',i);
if(j==-1) break;
mx=max(mx,j-i);
//i=max(i,j)+1;
int k=s.find(op[0],i+1);
i=max(k,j+1);
}
cout<<mx<<'\n';
}
}
return 0;
}
D:Divisibility by 2^n
算是第一个比较难的题(?),其实也不难,挺简单的。
看要求,乘起来要被2的n次方整除
那么就可以把每个值,求其能分解出多少个2,然后加起来,判断。因为相乘的话,能被2分解出来的个数就算是相加了么。
既然如此,改造一下lowbit函数,让其返回二进制中末位连续的0的个数(最后一个1所对应位-1)
这就代表能分解出来多少个2.
此处不选择开辟一个数组,将对应的lowbit的值放入对应的i中。否则的话,最后的贪心策略选择时不好选择。
选择开辟一个vector,将1-n的值全部lowbit后放入
从大到小排序亦可,从小到大倒着选择,不需要恰好凑齐,只需要大于即可满足能被整除。
代码如下
//存储每一个数字的最后一位1所在位置-1
//能被2的k次方整除,说明右移k位后末位为1
#include<bits/stdc++.h>
using namespace std;
int lowbit(int x){
int t=x&-x;
int cnt=0;
while(t){
t/=2;
cnt++;
}
return cnt-1;
}
int main(){
cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
//int a[50];
//memset(a,0,sizeof(a));
int sum=0;
for(int i=0;i<n;i++){
int k;
cin>>k;
//a[lowbit(k)-1]++;
sum+=lowbit(k);
}
//能被2的n次方整除
//我得看看他们加起来能不能达到
if(sum>=n) cout<<0<<'\n';
else{
int h=n-sum%n;
//现在还差几位
int num=0;
vector<int>cal;
for(int i=n;i>=2;i--){
cal.push_back(lowbit(i));
num+=lowbit(i);
}
if(num<h) cout<<-1<<'\n';
else{
int cnt=0;
num=0;
//贪心选择
sort(cal.begin(),cal.end());
for(int i=cal.size()-1;num<h&&i>=0;i--){
cnt++;
num+=cal[i];
}
cout<<cnt<<'\n';
}
}
}
return 0;
}
如果此处不选择用vector+sort解决
代码量会大大增加,而且容易想错。一个下标里面又有多个值,再加上边界问题的考虑,很容易弄错。
故采用vector+sort(感谢群内大佬指点,原来就卡在贪心策略的选择上面了)
E1,E2:
数论知识,不会,等题解
这两道题,EF,参考知乎dalao cup-pyy 的专栏吧
我得自己再研究研究,有些东西还没弄太懂
F:
不会,等题解