目录
一、AC情况
全部四道题赛后补题AC
二、赛中概况
第一道题就卡住了,有思路,但代码一直调不对。最后打了个表,10^9的数据量内存原地爆炸爆掉,第二题用不到20分钟秒了,但是freopen写错了(……)。后面两题直接连题都没读。
三、解题报告
问题一:操作队列(queue)
情况:
赛后补题AC
题意:
一个队列,初始有a,b,c,d,e五个元素,进行多次操作,每次将队首元素出队,并将该元素入队两次,给定一个数字n,求第n次操作时弹出的队首元素。
比赛时思路是对的,但代码没调出来。
题解:
第一种思路,直接用队列进行模拟。但不出意外的话肯定超时。
因此可以找规律。
不难看出,该队列是这样的:
阶段数 | 序列 | 长度 |
1 | abcde | 5 |
2 | aabbccddee | 10 |
3 | aaaabbbbccccddddeeee | 20 |
4 | …… | 40 |
那么我们可以得出一下思路:
求要查询的点在第几阶段,就让总长度n不断减去5、10、20、40……
求出在低级阶段,就很容易得出答案了。
正解代码:
#include<iostream>
using namespace std;
int n;
int x=5; //每阶段的长度
int y=1; //当前阶段每个字母的个数
int main(){
cin>>n;
while(n-x>0){
n-=x;
x*=2;
y*=2;
}
char c=(n+y-1)/y+'a'-1; //答案
cout<<c;
return 0;
}
问题二:数值计算(calc)
情况:
赛后补题AC
题意:
给定a,b,c,求出一个最小的k,使k满足(k^a)%b=c。如果k不存在,输出-1。
不要问我这个标题里a和l为什么标红,问就是比赛时freopen里打反了。
题解:
这题其实非常简单,只需要简单枚举k,用快速幂求出(k^a)%b,再判断是否与c相等即可。
一个需要关注的点在于,k最大不超过多少?
因为(k^a)%b=[(k%b)^a]%b,所以k>=b没有意义,会被模掉,所以k最大不超过,也不会等于b,即k<b。
那么这题就非常好写了。
正解代码:
#include<iostream>
#include<cstdio>
using namespace std;
long long n,a,b,c;
long long ksm(long long a,long long b,long long m){
long long ans=1%m;
while(b){
if(b&1){
ans=(ans*a)%m;
}
b>>=1;
a=(a*a)%m;
}
return ans;
}
int main(){
cin>>n;
while(n--){
cin>>a>>b>>c;
if(c>b){ //因为要模b,所以c不可能大于b,直接输出-1
cout<<-1<<"\n";
continue;
}
for(int i=1;i<b;i+=2){
if(ksm(i,a,b)==c){
cout<<i<<"\n";
break;
}
}
}
return 0;
}
问题三:益智游戏(game)
情况:
赛后补题AC
题意:
有n辆卡车,第i辆卡车有ti千克的货物,限重是wi。当车上货物的重量大于限重时,车子就会报废。我们的卡车是第一辆,我们可以把我们卡车上的货物放到别人的车上,使别人的卡车报废,让我们的排名更高。问我们能达到的最高名次是多少(游戏名次计算规则:在没有报废的车中,按照货物重量降序)。
题解:
这题使用贪心。
我们要破坏名次更高,且花费最少的卡车。
在报废卡车的过程中,因为货物放到了别人的车上,我们的排名有可能会降低,因此要动态维护排名。
使用优先队列进行维护即可。
正解代码:
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
//存储两个值,优先按照ti排序,确定谁名次更高
pair<long long,long long> p[300010];
int n;
long long t,w; //自己的,单独维护
priority_queue<long long,vector<long long>,greater<long long> > q; //小根堆
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>t>>w;
for(int i=2;i<=n;i++){
cin>>p[i].first>>p[i].second;
}
sort(p+2,p+1+n); //确定谁名次更高
long long ans=0x3f3f3f3f;
int i=n;
while(1){
//找到排名更高的放入优先队列
for(;i>=2&&p[i].first>t;i--){
//tmp存储需要多少可以使该车报废
long long tmp=p[i].second-p[i].first+1;
q.push(tmp);
}
ans=min(ans,(long long)q.size()+1);
if(!q.empty()&&t>=q.top()){
t-=q.top();
q.pop();
}
else{
break;
}
}
cout<<ans;
return 0;
}
问题四:异或函数(function)
情况:
赛后补题AC
题意:
在一个长度为n的数组中,定义异或函数:
其中⨁指的是异或。
给定区间[l,r],求出a[l],a[l+1],⋯,a[r]的子区间中异或函数f的最大值是多少。
题解:
分析:
如:1 2 4 8
1 2 4 8 3 6 12 5 10 15
类似于杨辉三角。
实现:
使用dp。
设数组f,f[i][j]表示第i层的第j个数字。
类比杨辉三角,推出f[i][j]=f[i-1][j-i]^f[i-1][j]。
递推求出所有f[i][j]。
设数组dp,dp[i][j]表示从第i个数字开始,长度为j的最大值。
用动态规划求出dp[i][j]。
最后进行查询即可。
正解代码:
#include<iostream>
using namespace std;
int f[5050][5050],dp[5050][5050],n;
int main(){
ios::sync_with_stdio(false); //关闭输入输出流,不然容易时间超限
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>f[1][i];
dp[i][1]=f[1][i];
}
for(int i=2;i<=n;i++){
for(int j=1;j<=n-i+1;j++){
f[i][j]=f[i-1][j]^f[i-1][j+1];
dp[j][i]=max(f[i][j],max(dp[j][i-1],dp[j+1][i-1]));
}
}
int q;
cin>>q;
int l,r;
while(q--){
cin>>l>>r;
cout<<dp[l][r-l+1]<<"\n";
}
return 0;
}
四、总结
这次比赛可以说打的很不好。第一题打表爆内存,第二题犯了十分低级的错误,后两题直接没看。
应合理分配时间,暴力不要想着拿全分,注意检查。