前言
仿佛这几天都没有在赛前放空大脑,都是瞅着有比赛就直接报名去打了。
Standings:2520
题目链接:Dashboard - Codeforces Round 961 (Div. 2) - Codeforces
A. Diagonals
题意:
给一个 n * n 的棋盘,你需要把若干个棋子放在棋盘的格子里,每个格子里最多只能放 1 个棋子。假设某个格子在第 i 行第 j 列,定义对角线为所有 (i + j)相等的格子的集合,问最少使用多少条对角线可以把所有棋子都装进去。
思路:
简单贪心,把对角线按其格子数从大到小排个序,依次插入棋子即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,k,m,a[10005],b[100005],cnt;
int cmp(int x,int y) { return x > y ; }
int max(int x,int y) { return x > y ? x : y ; }
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%d",&n,&k),m = n * n,cnt = 0;
for (int i = 1;i <= n;++ i)
for (int j = 1;j <= n;++ j) a[(i - 1) * n + j] = i + j;
sort(a + 1,a + m + 1),a[m + 1] = -1;
int now = 1;
for (int i = 2;i <= m + 1;++ i)
if(a[i] != a[i - 1] && i) b[++ cnt] = now,now = 1;
else ++ now;
sort(b + 1,b + cnt + 1,cmp);
now = 1;
while (k)
{
k = max(0,k - b[now]);
++ now;
}
printf("%d\n",now - 1);
}
return 0;
}
B1. Bouquet (Easy Version)
题意:
小女孩要在花店买一束花,花店有 n 朵花,每朵花有若干片花瓣,一片花瓣要花一块硬币。女孩希望买的所有花中,任意两朵之间的花瓣差不超过 1 。她有 m 块硬币,问最多可以买到多少片花瓣。
思路:
题目的限制 “ 任意两朵之间的花瓣差不超过 1 ” 其实简化了难度,这样我们要么就是只买花瓣一样的花,要么就是买花瓣数目相差 1 的花,按花朵的花瓣数排个序直接做即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200005
int T,n,a[N],b[N],c[N],cnt;
long long m,ans;
int min(int x,int y) { return x < y ? x : y ; }
long long max(long long x,long long y) { return x > y ? x : y ; }
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%lld",&n,&m),cnt = 0,ans = 0ll;
for (int i = 1;i <= n;++ i) scanf("%d",&a[i]);
sort(a + 1,a + n + 1),a[n + 1] = -1;
int now = 1;
for (int i = 2;i <= n + 1;++ i)
if(a[i] != a[i - 1]) b[++ cnt] = now,c[cnt] = a[i - 1],now = 1;
else ++ now;
now = 1,c[cnt + 1] = -1;
while (now <= cnt)
{
long long tmp;
if(c[now + 1] - c[now] > 1 || now == cnt) tmp = 1ll * min(m / c[now],b[now]) * c[now];
else
{
for (int i = 0;i <= b[now];++ i)
{
if(1ll * i * c[now] > m) break;
tmp = 1ll * i * c[now];
tmp += 1ll * min((m - tmp) / c[now + 1],b[now + 1]) * c[now + 1];
ans = max(ans,tmp);
}
}
ans = max(ans,tmp);
++ now;
}
printf("%lld\n",ans);
}
return 0;
}
B2. Bouquet (Hard Version)
题意:
其他背景都相同,只有给出花朵的方式不同,Easy Version 是一个一个花朵列出来,Hard Version 是直接给出不同数目的花瓣对应的花朵数目,1 <= n <= 2 * 10^5 。
思路:
思路和 Easy Version 一致,现在考虑如何快速计算选两种花朵的答案。
乍一看是背包问题,但是不是常规的背包,因为相当于只有两个物品,且两个物品的价值刚好相差 1 ,这说明存在简单快速的方法。假设两种花的花瓣数分别为 x 和 x + 1,我们贪心地想,先尽量填满 x ,再填 x + 1 。假设已经塞不下 x 了,说明也不可能塞得下 x + 1 了;若尽可能塞完后背包还有小于 x + 1 的空间,且还有没塞入的 x + 1 ,我们就考虑用尽可能多的 x + 1 替代 x ,这样每替换一次就多利用了 1 的空间。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 200005
int T,n;
long long m,ans;
struct Node
{
long long b,c;
}a[N];
long long min(long long x,long long y) { return x < y ? x : y ; }
long long max(long long x,long long y) { return x > y ? x : y ; }
int cmp(Node x,Node y) { return x.c < y.c ; }
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d%lld",&n,&m),ans = 0ll;
for (int i = 1;i <= n;++ i) scanf("%lld",&a[i].c);
for (int i = 1;i <= n;++ i) scanf("%lld",&a[i].b);
sort(a + 1,a + n + 1,cmp),a[n + 1].c = -1ll;
int now = 1;
while (now <= n)
{
long long tmp;
if(a[now + 1].c - a[now].c > 1 || now == n) tmp = 1ll * min(m / a[now].c,a[now].b) * a[now].c;
else
{
long long numx = min(m / a[now].c,a[now].b);
tmp = 1ll * numx * a[now].c;
long long numy = min((m - tmp) / a[now + 1].c,a[now + 1].b);
tmp += 1ll * numy * a[now + 1].c;
if(tmp < m && numy < a[now + 1].b) tmp += 1ll * min(numx,min(m - tmp,a[now + 1].b - numy));
}
ans = max(ans,tmp);
++ now;
}
printf("%lld\n",ans);
}
return 0;
}
C. Squaring
题意:
给一个序列 a ,每次操作可以让某个数变成它的平方,问至少用多少次操作能使序列非降。
思路:
朴素的想法是从左到右遇到一个不合法的就让它平方直至合法,但这样可能会超时且超过 long long 甚至 int64 的范围。自然而然想到可以取对数,在对数层面计算就不会超范围了。
设前一个数为 x ,当前这个数为 y ,若 x > y 我们就需要进行操作:
因此我们对每个数取两次 log 再操作即可。
Tips:
其实这道题还有整数的做法,我们不需要真正地将某个数平方那么多次,只需要记录每个数平方了多少次(记作 p)即可。我们需要在原始数组上预处理出每个数与他前面那个数在平方次数上相差多少次(记作 x),若前面那个数需要平方两次或以上才能超过当前这个数,那么 x 就是负数;若前面那个大于当前这个数,则 x 是正数;否则 x 是 0 。那么当前这个数的 p 就是 x 加上前面那个数的 p 。
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define N 200005
int T,n,p[N];
double a[N],b[N];
long long ans;
int main()
{
scanf("%d",&T);
while (T --)
{
scanf("%d",&n);
for (int i = 1;i <= n;++ i) scanf("%lf",&a[i]),b[i] = log(log(a[i])),p[i] = 0;
ans = 0ll;
int flag = 1;
for (int i = 2;i <= n;++ i)
{
double x = b[i - 1];
double y = b[i];
if(y < x)
{
if(a[i] == 1.00)
{
flag = 0;
break;
}
p[i] = floor((x - y - 1e-9) / log(2)) + 1;
b[i] += log(2) * p[i];
}
}
if(!flag)
{
printf("-1\n");
continue;
}
for (int i = 1;i <= n;++ i) ans += 1ll * p[i];
printf("%lld\n",ans);
}
return 0;
}
剩下的题以后再补,挖个坑 。
总结
这场比赛 C 其实已经做出来了,但是比赛玄学时间总是让我慢了几秒钟,总的来说还是时间的把控要再磨炼,前面简单题和思维题的解题速度要提升。