最近CodeForces比赛有点多,有点刺激,这次A了三题,不过思路太慢了,而且理解题意整的不好
A题 : 自己没耐心写下去,结果好久好久才过,下次遇到这种题一定记得
解题思路:
- 主要是暴力,给你一个值就第 k 项的值
- 根据这个式子往后计算就可以,因为每次加的数 1 ~ 81 ,所以百位不是+ 0 就是 + 1,所以最多1000次,他就为将改为变为0,如果存在0,那么他的值肯定就不会变换了,就是这样!
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
int main(){
int t;
scanf("%d",&t);
while(t--){
ll a, k;
scanf("%lld%lld",&a,&k);
for (ll i = 1; i <= k -1; i ++){
ll b = a;
int mi = 10;
int mx = 0;
while(b){
int t = b % 10;
mi = min(mi, t);
mx = max(mx, t);
b/=10;
}
a = a + mi * mx;
if (mi * mx == 0) {
break;
}
}
printf("%lld\n",a);
}
return 0;
}
B题: B题感觉就是自己读题的问题了,对于其中一段话理解有错误,感觉以后还是要多读英文题
解题思路:
- 题目的大意,一个人的e值,代表这个人可以加入一个组的最少人数(这个组的人数为e或者更多)
- 因此我们对e从小到大排序,我们只要判断当前队伍人数是否 >= e 值即可,如果是,那么就创建一个队伍出来,最后可以剩人
- (主要是理解题意)
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 200010;
int a[N];
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for (int i = 1; i <= n; i++){
scanf("%d",&a[i]);
}
sort(a+1,a+1+n);
int res = 0;
int num = 0;
for (int i = 1; i <= n; i++){
res ++;
if (res >= a[i]){
num ++;
res = 0;
}
}
printf("%d\n",num);
}
}
C题:有很多种解法,这里写一下差分的解法吧(因为比较简单一些),也可以"暴力"
解题思路:
- 首先我们从A ~ B 模拟第一个数值,那么我们第二个数值的范围是 B ~ C
- 所以两个数值的和的范围为 A + B ~ B + C
- 我们枚举第一个位置的值,然后我们将i + B~ i + C 这个范围的值做差分,然后一直枚举完
for (int i = a; i <= b; i++){
ans[i + b] ++;
ans[i + c + 1] --;
}
- 然后我们进行前缀和处理
for (int i = 1; i < N ; i++){
ans[i] += ans[i - 1];
}
- 然后思考,我们第三个值是从C + 1开始,那么前面满足的就为ans[i] 个,然后乘第三个值符合的区间数即可
- 需要注意的是这里的符合区间的值一定是小于C + 1的,因为两边之和大于第三边,然后如果有值超出d,那么前面所有的数值都符合,所有这里注意下范围,可能达到最大的1e6,
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1000010;
ll ans[N];
int main(){
int a, b, c, d;
scanf("%d%d%d%d",&a,&b,&c,&d);
for (int i = a; i <= b; i++){
ans[i + b] ++;
ans[i + c + 1] --;
}
for (int i = 1; i < N ; i++){
ans[i] += ans[i - 1];
}
ll res = 0;
for (int i = c + 1; i < N ;i ++){
ll t = min(i - 1, d) - c + 1;
res += ans[i] * t;
}
printf("%lld\n",res);
return 0;
}
D题: 这题有些迷,虽然知道答案,但是不太懂怎么证明的,大体说一下
解题思路:
- 首先要能取出这个值,就让他前面全为1(就是这样),因为我想让他取不出来。
- 然后肯定存在n - 1 个 1,然后最后一个值就为 s - (n - 1),所以我们想让有些值不存在我们就让
n − 1 > = s − ( n − 1 ) − 1 n\,\, -\,\,1>=\,\,s\,\,-\,\,\left( n\,\,-\,\,1 \right) \,\,-\,\,1 n−1>=s−(n−1)−1
2 n > s 2 n\,\,>\,\,s 2n>s - 这种就可以找到,输出no即可
- 其他的都不可,输出n - 1 个 1,和 s - n + 1即可 (具体证明没太明白)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
int main(){
int n, s;
scanf("%d%d",&n,&s);
if (s < 2 * n){
puts("NO");
}
else{
puts("YES");
for (int i = 1; i <= n - 1; i++){
printf("%d ",1);
}
printf("%d\n",s - n + 1);
printf("%d\n",n);
}
return 0;
}
E题:三分,但是不太会证明他为什么是凹函数
解题思路:
- 这里是三分高度,最小为0,最大为1e9
- 然后如果小了的话会花费更多来达到相同的高度,如果大了也会导致相同的情况,所以中间是最好的(但是具体证明还不太明白)
- 所以我们直接三分就OK,输出他的花费来进行移动
- 这里注意一下一次 m 就相当于 a + r ,所以我们m = min(m,a + r) 即可
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 100010;
ll h[N];
ll n, a, r, m;
ll check(ll x){
ll ans = 0; // need to add
ll res = 0;
for (int i = 1; i<= n; i ++){
if (h[i] < x){
ans += x - h[i];
}
else{
res += h[i] - x;
}
}
ll t = min(ans,res);
ll num = t * m + (ans - t) * a + (res - t) * r;
return num;
}
int main(){
scanf("%lld%lld%lld%lld",&n,&a,&r,&m);
for (int i = 1; i <= n; i++) scanf("%lld",&h[i]);
m = min(m, a + r);
int l = 0, r = 1e9;
while(l < r){
int lx = l + (r - l)/3;
int rx = r - (r - l)/3;
if (check(lx) < check(rx)){
r = rx - 1;
}
else{
l = lx + 1;
}
}
printf("%lld\n",check(l));
}