2024牛客暑期多校训练营9 - VP记录
A. Image Scaling
签到题,找出举行宽高以后直接除以它们的 \(\gcd\) 使它们互质即可。
(这道题居然会有人又 WA 又 RE,我不说是谁)
点击查看代码
#include<cstdio> #include<cstring> using namespace std; const int N=505; int n,m,x1,y1,x2,y2; char g[N][N]; int gcd(int x,int y){return y ? gcd(y,x%y) : x;} int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",g[i]+1); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) if(g[i][j]=='x') { x1=i,y1=j; break; } if(x1) break; } for(int i=n;i>=1;i--) { for(int j=m;j>=1;j--) if(g[i][j]=='x') { x2=i,y2=j; break; } if(x2) break; } int h=x2-x1+1,w=y2-y1+1; int d=gcd(w,h); w/=d,h/=d; for(int i=1;i<=h;i++) { for(int j=1;j<=w;j++) putchar('x'); putchar('\n'); } return 0; }
B. Break Sequence
这道题有点意思,单独拎出来写了一篇题解,注释很详细:点击这里。
I. Interesting Numbers
题意:把一个数从正中间劈开,两边两个数都是平方数,这样的数在 \([L,R]\) 内有多少个?
赛时读题没读懂,以为是乘积或者加和,晕🤮。
运用前缀和的思想,这道题很容易转化成“\([1,R]\) 中满足条件的数的数量”-“\([1,L-1]\) 中满足条件的数的数量”。
首先这个数一定有 \(N\) 位,其中 \(N\) 为偶数。
因为这个数的左右两半是独立不互相影响的,所以可以分开考虑。
设右边界 \(R\) 的左右两半为 \(R1\) 和 \(R2\),当前数 \(X\) 的左右两半为 \(X1\) 和 \(X2\)。劈成两半后最多 \(30\) 位,刚好可以用 __int128
存(最高存 \(38\) 位)。
当 \(X1<R1\) 时,\(X2\) 无论怎么取 \(X\) 都小于 \(R\),此时:
- \(X1\) 共有 \((\lfloor\sqrt{R1-1}\rfloor+1)\) 种选择;减一是因为不能等于 \(R1\),加一是因为可以包括 \(0\)。
- \(X2\) 共有 \((\lfloor\sqrt{10^{\frac{N}{2}}-1}\rfloor+1)\) 种选择;\((10^{\frac{N}{2}}-1)\) 可以构成 \(99 \cdots 999\) 这样的结构,加一也是因为可以包括 \(0\)。
每一个 \(X1\) 和 \(X2\) 都可以任意组合,所以这时的总选择数就是两者的乘积 \((\lfloor\sqrt{R1-1}\rfloor+1)(\lfloor\sqrt{10^{\frac{N}{2}}-1}\rfloor+1)\)。
当 \(X1=R1\) 时,首先需要保证 \(R1\) 是平方数,否则这种情况不成立,此时:
- \(X1\) 仅有一种可能。
- \(X2\) 必须小于等于 \(R2\),所以此时 \(X2\) 共有 \((\lfloor\sqrt{R2}\rfloor+1)\) 种选择,加一同样是因为可以取到 \(0\)。
此时共有 \((\lfloor\sqrt{R2}\rfloor+1)\) 种选择。
上面两种(或当 \(R\) 不为平方数时仅第一种)情况相加就求出了 \([1,R]\) 中满足条件的数的数量;\([1,L-1]\) 同理,使 \(L1\) 不变,\(L2\) 减一再计算即可。
注意一下 \(L2=0\) 时的处理,我这里的处理可以看代码注释。
#include<cstdio> using namespace std; const int N=105; int n; char ls[N],rs[N]; __int128 sqrt_int128(__int128 x) //此处 sqrt(x<0)=-1 { __int128 l=-1,r=1e15+1; //不能赋为x+1!平方会爆范围! while(l+1<r) //l*l<=x; r*r>x { __int128 mid=(l+r)>>1; if(mid*mid<=x) l=mid; //mid*mid 会爆int128!!! else r=mid; } return l; //自动向下取整 } __int128 quick_pow_int128(__int128 x,__int128 y) { __int128 res=1; while(y) { if(y&1) res*=x; x*=x; y>>=1; } return res; } __int128 get_num(int n,__int128 r1,__int128 r2) { __int128 res=0; res += (sqrt_int128(r1-1)+1) * (sqrt_int128(quick_pow_int128(10,n>>1)-1)+1); res += (sqrt_int128(r1)-sqrt_int128(r1-1)) * (sqrt_int128(r2)+1); //非平方数得0,r2=-1时也得0 return res; } void write_int128(__int128 x) { if(x>9) write_int128(x/10); putchar('0'+x%10); } int main() { scanf("%d%s%s",&n,ls+1,rs+1); __int128 l1=0,l2=0,r1=0,r2=0; for(int i=1; i<=n>>1;i++) l1=l1*10+(ls[i]-'0'); for(int i=(n>>1)+1; i<=n; i++) l2=l2*10+(ls[i]-'0'); for(int i=1; i<=n>>1;i++) r1=r1*10+(rs[i]-'0'); for(int i=(n>>1)+1; i<=n; i++) r2=r2*10+(rs[i]-'0'); __int128 ans=get_num(n,r1,r2)-get_num(n,l1,l2-1); write_int128(ans); return 0; }
K. Kill The Monsters
贪心,每次一定优先对最大的那个进行操作二,而操作一的次数应当是剩下所有数的最大值。
所以模拟砍最大数的过程(可以用优先队列找最大数),然后统计操作次数并对所有的 \((\max a + alr)\) 取最小值(其中 \(alr\) 表示已经进行过的操作次数)。
因为没有判断全部进行操作二的可能性而 WA 了好多发,最后应当用最终的 \(alr\) 再更新一边答案。
每次操作都将其中一个除以 \(k\),所以时间复杂度为 \(O(N \log_K \max a_i )\)
还要注意特判 \(k=1\) 的情况,此时无法进行操作二,只需取最大值即可。
#include<cstdio> #include<queue> #include<algorithm> using namespace std; const int N=1e5+5; int n; long long k,a[N]; priority_queue<long long> pq; int main() { scanf("%d%lld",&n,&k); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); pq.push(a[i]); } if(k==1) { printf("%lld\n",pq.top()); return 0; } long long ans=1e18,alr=0; while(!pq.empty()) { long long x=pq.top(); pq.pop(); ans=min(ans,x+alr); x/=k; if(x>0) pq.push(x); alr++; } printf("%lld\n",min(ans,alr)); return 0; }
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18493111
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步