2024年6月赛丙组
丙组6月卷涉及了:枚举、位运算、简单数论、数学或宽搜、单调队列优化。
按CCF考纲来看,已经超出入门组范畴,属于入门组+或提高组-难度。
T1 布置会场(二)
可以用选择结构解决,但是漏情况会错。
数据并不大,不妨用枚举遍历所有情况来寻找答案。
一束花用
#include<bits/stdc++.h>
using namespace std;
int n,a,b,c,ans=2e9;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>a>>b>>c;
for(int i = 0; i <= n/c+1; i++){
int m = n - i*c;
int tmp = i*b;
if(m>0) tmp += m*a;
ans = min(ans,tmp);
}
cout<<ans;
return 0;
}
为什么枚举的范围是
T2 位运算
还没接触过位运算的会无从下手。
来看下需要满足的三个条件:
题目要求找出最小的正整数
第二个条件,用第一个条件找出来的
第三个条件是异或,异或是当二进制位不同的时候结果为
共有两种情况:
有多个二进制位 ,那么最终答案还是 最后一个二进制位 ,因为 其他二进制位为 ,而 有多个二进制位 ,那么异或结果必然不等于 。 只有一个二进制位 ,也有两种情况:(1) 在最后一位,那么要在前 位补 ,即答案 。(2) 不在最后一位,那么最后 位补
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
m = n & -n;
if(__builtin_popcount(n)>1) cout<<m;
else{
if(m==1) cout<<3;
else cout<<(m|1)<<endl; //或者输出m+1
}
return 0;
}
学过树状数组的知道n&-n
可以算出
T3 完全平方数对
分解质因数+思维
应该是这份试题中最优价值的一题。
题意:两个因数相乘,因数不超过
对整数
如果直接枚举,
完全平方数的中不同的质因数必须偶数个才能形成完全平方数。
例如
根据这个思路,我们可以先对
那么
以
显然最小的
最大的数对为
也就是
从因数角度入手,只要因数成双的,它们的乘积必然是完全平方数。
完全平方数 | i | j | 补的因数 | 最终算式 | 数对 |
---|---|---|---|---|---|
通过上面的例子可以发现规律,找到最小的
那么在整数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,ans;
void divid(int x)
{
int tx = x, res = 1; //res用来求最小的数对(i,res)
for(int i = 2; i <= sqrt(x); i++){
int cnt = 0; //cnt用来统计相同质因数个数
while(x % i==0) x /= i,cnt++;
if(cnt % 2==1) res *= i;
}
if(x > 1) res *= x;
ans += int(sqrt(tx / res)) * 2; //最终符合要求的数对
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i = 1; i <= n; i++) divid(i);
cout<<ans - n<<endl; //扣除n对(i,i)
return 0;
}
T4 超级奇数
数学规律或宽搜
这里讲一下宽搜做法。
超级奇数只有1、3、5、7、9组成,最大为
可以发现并不多,所以直接用宽搜扩展寻找即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,ans,b[]={1,3,5,7,9},cnt;
queue<ll> q;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i = 0; i < 5; i++) q.push(b[i]);
while(q.size()){
ll x = q.front();
q.pop();
cnt++;
if(x == n){
cout<<cnt<<endl;
return 0;
}
for(int i = 0; i < 5; i++){
ll t = x * 10 + b[i];
q.push(t);
}
}
return 0;
}
T5 环岛旅行
单调队列优化
这题是一本通旅行问题的简化版。
环形问题,先破环成链,即用
例如,假设
序列编号为
能否到达取决于油量是否够,所以途中的任意一个加油站,存储的油量必须大于等于消耗的油量,才能到达下一个加油站。
设
考虑顺时针情况,
那么在区间
破环成链和前缀和
for(int i = 1; i <= n; i++){
cin>>p[i]>>c[i];
p[n+i] = p[i];
c[n+i] = c[i];
}
for(int i = 1; i < 2 * n; i++) sum[i] = sum[i-1] + p[i] - c[i];
单调队列维护
int head = 0, tail = 0;
for(int i = 1; i <= 2 * n; i++){
while(head <= tail && i - q[head] >= n) head++;
while(head <= tail && sum[q[tail]] >= sum[i]) tail--;
q[++tail] = i;
if(i >= n && sum[q[head]] - sum[i - n] >= 0){ //i为1的时候表示走向2,所以i为n的时候表示走向1
cout<<i - n + 1;
return 0;
}
}
cout<<0<<endl; //没有符合要求的起点
因为是顺时针,所以当