Spring2023训练
3.4
A#
A
只能购买2的次方数的纸张,而他最大可以购买n大小的纸张
也就是求小于n的最大二次方数
#include<bits/stdc++.h>
using namespace std;
int main(){
int x;
cin>>x;
int d=(int)log2(x);
cout<<pow(2,d);
return 0;
}
B#
B
已知她最多可以吃多少糖,再给出每一个巧克力的含糖量,求她最多可以吃多少巧克力
直接排序即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[110],b[1010],c[1010];
int32_t main(){
int n,s,d,sum=0;
cin>>n;
cin>>s;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
sum+=a[i];
if(sum>s) {
d=i;
break;
}
}
cout<<d-1<<endl;
return 0;
}
C#
C
已知圆心坐标和半径,再给n个点的坐标,求有多少个点在圆内
使用
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[110],b[1010],c[1010];
int32_t main(){
int n,cx,cy,r,x,y,ans=0;
cin>>n>>cx>>cy>>r;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x,&y);
if((x-cx)*(x-cx)+(y-cy)*(y-cy)<=r*r)
ans++;
}
cout<<ans<<endl;
return 0;
}
D#
D
这道题赛时没做出来,以为是个思维题,一直在找规律,补题发现是dp。题意是给你一串只由T和C组成的字符串,将它分成T,C,TC,CC的小段,有多少种分法。其实这个题可以发现他只分成长度为一的和长度为二的字符串,那么其中一位下标就可以存为当前这一位前面还有几个字母没有组合,只能为0和1,另一位下标存当前在哪一位,然后分情况讨论,如果当前这一位是T,那无论前一位有没有字母没有组合,它都不能和之前组,只能自己一组,或者自己和后面组,dp[x][1]=0;如果是C,无论前面是哪个字母都可以组,所以只要前一位是被单独留下的即可,想要被单独留下,那前一位的前面不能留下字母,dp[x][i]=dp[i-1][0],而他俩前面不留字母的情况是完全一样的,任何情况都可以,不论前面留不留字母,他都可以单独dp[x][0]=d[x-1[0]+dp[x-1][1]
#include<bits/stdc++.h>
using namespace std;
#define int long long
int f[1000010][2];
int32_t main(){
int mod=1e9+7;
string s;
cin>>s;
int n=s.length();
f[0][0]=1;
f[0][1]=0;
for(int i=1;i<s.length();i++){
if(s[i]=='T'){
f[i][0]=(f[i-1][0]+f[i-1][1])%mod;
f[i][1]=0;
}
else{
f[i][0]=(f[i-1][0]+f[i-1][1])%mod;
f[i][1]=f[i-1][0];
}
}
cout<<(f[n-1][0]+f[n-1][1])%mod<<endl;
return 0;
}
E#
E
这一天共召开n场会议,每一次会议都会提供a,b,c个idea,他不能连着开两场会议,怎么安排会议可以解决最多的idea,首先每一次开会处理的idea一定是三个数中的最大值。在选择的时候可以选他的x+2或x+3场,当选择x+4时,可以选完x+2再选是更优的情况。递推表达式就是dp[i]=max(dp[i-2],dp[i-3])+a[i];这里注意最后一场会议有可能是倒数第二场,输出结果应该是max(dp[n],dp[n-1]
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[1010],b[1010],c[1010],d[1010],s1[1010],s2[1010],dp[1010];
int32_t main(){
int n,ans=0;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&b[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&c[i]);
d[i]=max(max(a[i],b[i]),c[i]);
//cout<<i<<" "<<d[i]<<endl;
}
if(n==1){
cout<<d[1]<<endl;
return 0;
}
else if(n==2){
cout<<max(d[1],d[2])<<endl;
return 0;
}
dp[1]=d[1];
dp[2]=d[2];
for(int i=3;i<=n;i++){
dp[i]=max(dp[i-2]+d[i],dp[i-3]+d[i]);
}
cout<<max(dp[n],dp[n-1])<<endl;
return 0;
}
F#
F
这道题为多组数据,每组提供b,l,求这两个数的gcd并把它分解质因数,注意每一组数据输出后面都有一行0
#include<bits/stdc++.h>
using namespace std;
#define int long long
int32_t main(){
int T,b,l;
cin>>T;
while(T--){
cin>>b>>l;
int x=__gcd(b,l);
int y=sqrt(x);
for(int i=2;i<=y;i++){
int ans=0;
while(1){
if(x%i!=0)
break;
else
x/=i,ans++;
//cout<<x<<" "<<i<<" "<<ans<<endl;
}
if(ans!=0)
cout<<i<<" "<<ans<<"\n";
}
if(x>1)
cout<<x<<" 1\n";
cout<<0<<"\n";
}
return 0;
}
3.7
A#
把所有情况都敲出来就行
B#
联合权值,求A-B-C这样的路径和,正反向都有,那就用到了一个公式
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200010];
set<int>s[200010];
int32_t main(){
int n,l,r,sum=0,mod=10007,max1=0;
cin>>n;
for(int i=1;i<n;i++){
cin>>l>>r;
s[l].insert(r);
s[r].insert(l);
}
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
int summ=0,sum1=0,max11=0,max12=0;
for(auto e:s[i]){
summ=(summ+a[e])%mod;
sum1=(sum1+a[e]*a[e])%mod;
if(a[e]>=max11){
max12=max11;
max11=a[e];
}
else if(a[e]>max12){
max12=a[e];
}
}
sum=((summ*summ%mod-sum1%mod+sum%mod)%mod+mod)%mod;
max1=max(max1,max11*max12);
//cout<<summ<<" "<<sum1<<" "<<sum<<endl;
}
cout<<max1<<" "<<sum%mod<<endl;
return 0;
}
D#
这道题就是二维前缀和,要注意它是以当前位置为中心,上下各延伸d,要注意判断边界,不要越界。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[130][130],b[130][130];
int32_t main(){
int d,n,ans=0,sum=0;
cin>>d;
cin>>n;
int x,y,k;
int p=0,q=0;
for(int i=1;i<=n;i++) {
cin >> x >> y >> k;
a[x+1][y+1] = k;
}
for(int i=1;i<=129;i++) {
for (int j = 1; j <= 129; j++) {
b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + a[i][j];
}
}
for(int i=1;i<=129;i++){
for(int j=1;j<=129;j++){
int xl=max((int)1,i-d),xr=min((int)129,i+d);
int yl=max((int)1,j-d),yr=min((int)129,j+d);
int xx;
xx=b[xr][yr]-b[xl-1][yr]-b[xr][yl-1]+b[xl-1][yl-1];
if(xx>sum){
ans=1;
sum=xx;
//cout<<i<<" "<<j<<" "<<sum<<endl;
}
else if(xx==sum){
ans++;
}
}
}
cout<<ans<<" "<<sum<<endl;
return 0;
}
G#
这道题就是规律题,每次除以2,看它是出现在后一半还是前一半,若是后一半就是1,前一半就是0,递推到最后一位。要注意数据范围是
3.11
A#
A
这道题要求a^b=0,那就是a=b
#include<bits/stdc++.h>
using namespace std;
#define int long long
map<int,int>mp;
set<int>s;
int a[100010];
int32_t main(){
int n,ans=0;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
mp[a[i]]++;
s.insert(a[i]);
}
sort(a+1,a+1+n);
for(auto i:s){
int d=(0^i);
//cout<<i<<" "<<d<<endl;
if(s.count(d)){
ans+=mp[i]*mp[d];
}
}
cout<<ans<<endl;
return 0;
}
B#
B
这道题是贪心,因为每天必须吃一个苹果,那么选哪个吃的贡献其实是两个时间的贡献值的差值,晚上比白天多的越多贡献越大,那就按差值排序贪心。原来的假思路是dp,结果发现[100010][100010]的数组存不下。
#include<bits/stdc++.h>
using namespace std;
struct node{
int a,b,c;
}a[100010];
bool cmp(node a,node b){
if(a.c==b.c){
return a.b>b.b;
}
else{
return a.c>b.c;
}
}
int main(){
int n,k,ans=0;
cin>>n>>k;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].a,&a[i].b);
a[i].c=a[i].b-a[i].a;
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
if(i<=k){
ans+=a[i].b;
}
else{
ans+=a[i].a;
}
//cout<<i<<" "<<ans<<endl;
}
cout<<ans<<endl;
return 0;
}
C#
C
这个题其实和n皇后问题没啥关系横竖只需要看这个x和y有没有出现过即可,两条斜线斜率是1和-1,那只需要存与x轴交点的值即可,要注意这个题当这一步不能执行时这一步是不操作的。怎么会有人每次存完各个部位的值都要排一下序啊!!!!
#include<bits/stdc++.h>
using namespace std;
set<int>s1,s2,s3,s4;
int main(){
int n,T;
cin>>n>>T;
int x,y,f=0;
for(int i=1;i<=T;i++){
scanf("%d%d",&x,&y);
int h1=y-x;
int h2=x+y;
if(s1.count(x)||s2.count(y)||s3.count(h1)||s4.count(h2)){
cout<<"No\n";
f=1;
}
else{
s1.insert(x),s2.insert(y),s3.insert(h1),s4.insert(h2);
cout<<"Yes\n";
}
}
return 0;
}
D#
D
分苹果其实就是代入两条直线的公式里,因为苹果不会在直线上,所以只有两个结果
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans[10];
int32_t main(){
int n,ae,be,ce,ar,br,cr;
cin>>n;
cin>>ae>>be>>ce;
cin>>ar>>br>>cr;
int x,y;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x,&y);
int d1=ae*x+be*y+ce;
int d2=ar*x+br*y+cr;
//cout<<x<<" "<<y<<" "<<d1<<" "<<d2<<endl;
if(d1>0){
if(d2>0) ans[4]++;
else ans[2]++;
}
else if(d1<0){
if(d2>0) ans[3]++;
else ans[1]++;
}
}
sort(ans+1,ans+1+4);
for(int i=1;i<=4;i++){
cout<<ans[i]<<" ";
}
return 0;
}
3.12
A#
A
这道题就是给你一个字符串,字符串排列规则是必须以m或M开头,然后必须是e或E,然后必须是o或O,最后必须以w或W结尾,注意,每个字母都可以出现很多次。可以先把字符串全部转为小写字母并把连续的字母去重。以上是题解做法,我之前wa了的做法是和这四个字母比较,不一样就后移一个字母,如果四个字母都已经被比较过了但字符串仍未结束那就是比较出现了问题,还没想明白为啥wa了
#include<bits/stdc++.h>
using namespace std;
char d[]={'m','e','o','w'};
char D[]={'M','E','O','W'};
int ans[5];
int main(){
int T;
cin>>T;
while(T--){
int n;
cin>>n;
string s,ss="";
cin>>s;
for(int i=0;i<n;i++){
if(s[i]>='A'&&s[i]<='Z')
s[i]=s[i]-'A'+'a';
if(s[i]==s[i-1])
continue;
else
ss=ss+s[i];
}
if(ss!="meow")
cout<<"No\n";
else
cout<<"Yes\n";
}
return 0;
}
B#
B
这道题是字母配对,每一个小写字母都可以和它对应的大写字母匹配,每个字母只能匹配一次,直接统计每个字母出现了多少次,大小写分开记录,数组只需要开30大小,大小写字母的数值差除以2就是可以新组成的对数,注意k的大小。
#include<bits/stdc++.h>
using namespace std;
int d[30],D[30];
int main(){
int T;
cin>>T;
while(T--){
memset(d,0,sizeof(d));
memset(D,0,sizeof(D));
int n,k;
string s;
cin>>n>>k;
cin>>s;
for(int i=0;i<n;i++){
if(s[i]>='a'&&s[i]<='z'){
d[s[i]-'a']++;
}
else if(s[i]>='A'&&s[i]<='Z'){
D[s[i]-'A']++;
}
}
int ans=0;
for(int i=0;i<26;i++){
ans+=min(d[i],D[i]);
int dd=abs(d[i]-D[i]);
dd/=2;
if(dd<=k){
ans+=dd;
k-=dd;
}
else{
ans+=k;
k=0;
}
//cout<<d[i]<<" "<<D[i]<<" "<<i<<" "<<ans<<endl;
}
cout<<ans<<endl;
}
return 0;
}
C1C2#
C1
C2
这两道题是一样的,就是数据范围不一样,题目意思是你会拿到一些牌,如果是bonus牌,就可以放在一摞牌的最上面,在拿到hero牌时就可以把这个值赋给hero,那这个值就是我可以获得的power值。先说easy版思路,在遇到0之前的所有值放在vector里,然后遇到0时排序取最大值。hard版就是把出现的值存在set里然后用map统计有几个,每次遇到0直接取set结尾元素即可。注意会出现没有bonus牌但此时拿到hero牌的情况。
直接放hard版的代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
vector<int>ve;
set<int>s;
map<int,int>mp;
int32_t main(){
int T,n,x;
cin>>T;
while(T--){
s.clear();
mp.clear();
int ans=0;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld",&x);
if(x==0){
if(s.empty()){
continue;
}
else{
auto it=s.end();
it--;
int d=*(it);
mp[d]--;
if(mp[d]==0)
s.erase(d);
ans+=d;
}
//cout<<i<<" "<<ans<<endl;
}
else{
if(s.count(x)){
mp[x]++;
}
else{
s.insert(x);
mp[x]++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
D#
这道题就是删去连续的两个字母后能得到多少个不同的字符串。其实就是找aba形式的字符组合,这个组合删前面和删后面得到的字符串完全一样,如果不去重的话就是n-1在这个基础上把aba的组数减去即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
int32_t main(){
int T;
cin>>T;
while(T--){
int f=0;
int n,ans=0;
string s;
cin>>n;
cin>>s;
int sum=n-1;
for(int i=2;i<n;i++) {
if(s[i]==s[i-2])
sum--;
}
cout<<sum<<endl;
}
return 0;
}
3.21
A#
A
这道题会给你一个字符串,这个字符串是由原始字符串每次在首尾都加一个字符得到,且每次加字符时必须是一个1一个0,求原字符串有多长,就是题目难读懂,读懂之后也没啥问题
#include<bits/stdc++.h>
using namespace std;
#define int long long
int32_t main(){
int t;
cin>>t;
while(t--){
int n,ans=0;
string s;
cin>>n>>s;
int l=0,r=n-1;
while(l<=r){
if(s[l]!=s[r])
ans++;
else{
break;
}
l++;
r--;
}
cout<<n-2*ans<<endl;
}
return 0;
}
B#
B
这道题可以求一遍前缀和和一遍后缀和,就存到现在为止有多少种字母出现过,然后把整个字符串跑一遍,求最大值即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
map<char,int>mp;
int b[200010],c[200010];
int32_t main(){
int t;
cin>>t;
while(t--){
memset(b,0,sizeof(b));
memset(c,0,sizeof(c));
int n,max1=0;
string s;
cin >> n >> s;
mp.clear();
for (int i = 0; i < n; i++) {
if (mp[s[i]])
b[i] = b[i - 1];
else
mp[s[i]]++,b[i] = b[i - 1] + 1;
}
mp.clear();
for (int i = n - 1; i >= 0; i--) {
if (mp[s[i]])
c[i] = c[i + 1];
else
mp[s[i]]++,c[i] = c[i + 1] + 1;
}
for(int i=0;i<n;i++){
max1=max(b[i]+c[i+1],max1);
}
cout<<max1<<endl;
}
return 0;
}
C#
C
这道题就是选择两个相邻的数字,把他们都变成相反数,但要注意,可以进行无数次,所以其实是任意两个值同时翻转,那就分为两个情况,一种就是负数有偶数个,直接翻转,负数有奇数个就要留一个负数,那就选择绝对值最小的数成为负数,这里注意,把0当做负数
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[200010];
vector<int>ve;
int32_t main(){
int t;
cin>>t;
while(t--){
ve.clear();
int ans=0,n,sum=0,min1=LLONG_MAX;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum+=abs(a[i]);
min1=min(min1,abs(a[i]));
if(a[i]<0)
ans++;
}
if(ans&1){
sum-=2*min1;
}
cout<<sum<<endl;
}
return 0;
}
E#
E
这个题也是题意比较难读懂,其实就是给你4组a,b,c,d,贿赂每一个警务的两个物品价值选择价值更低的,然后要注意找到之后输出的不是两个最小值,而是一个最小值和n-最小值
#include<bits/stdc++.h>
using namespace std;
#define int long long
int32_t main(){
int n,f=0,a,b,c,d;
cin>>n;
for(int i=1;i<=4;i++){
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
if(f==1)
continue;
int x=min(a,b),y=min(c,d);
if(x+y<=n)
{
f=1;
cout<<i<<" "<<x<<" "<<n-x<<endl;
}
}
if(f==0)
cout<<-1<<endl;
return 0;
}
3.25 天梯选拔
前几题是语法基础,不写题解了
1-7#
1-7
这道题操作一就是把两个字母交换,操作二就是把前后字符串交换,交换两次相当于没交换,所以只需要进行操作一即可,操作二只需要在结束时判断一下是否需要前后交换,而操作一要注意定位当前的位置是不是你需要的字符,那就要判断当前操作二进行了几次。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int32_t main(){
int n,t,ans=0;
string s;
cin>>n;
cin>>s;
cin>>t;
s='0'+s;
int x,y,p;
while(t--){
scanf("%lld%lld%lld",&p,&x,&y);
if(p==2){
ans++;
}
else{
if(ans&1){
if(x>n)
x-=n;
else
x+=n;
if(y>n)
y-=n;
else
y+=n;
}
//cout<<s[x]<<" "<<s[y]<<endl;
char c=s[x];
s[x]=s[y];
s[y]=c;
//cout<<s[x]<<" "<<s[y]<<endl;
}
}
if(ans&1){
cout<<s.substr(n+1)<<s.substr(1,n)<<endl;
}
else
cout<<s.substr(1)<<endl;
return 0;
}
L2-1#
这道题就是并查集,find函数和fa[]数组,注意那个游戏圈数量就是有多少个不同的祖先,板子题就不放代码了
L2-3#
2-3
一眼dp题,每一位数是多少其实都是由前一位和这一位是多少决定,而前一位又是它前一位和自己运算得到,符合递推条件。dp[]数组第一位存当前是第几位,第二位存和前一位运算完值是多少,又因为每一位的值只有0-9,直接循环即可。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[100010][15];
int32_t main(){
int n,mod=998244353,x;
cin>>n;
scanf("%lld",&x);
dp[1][x]=1;
for(int i=2;i<=n;i++){
scanf("%lld",&x);
for(int j=0;j<=9;j++){
dp[i][(j+x)%10]=(dp[i-1][j]+dp[i][(j+x)%10])%mod;
dp[i][(j*x)%10]=(dp[i-1][j]+dp[i][(j*x)%10])%mod;
}
}
for(int i=0;i<=9;i++){
cout<<dp[n][i]%mod<<endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】