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;
}
posted @ 2024-10-22 16:19  Jerrycyx  阅读(15)  评论(0编辑  收藏  举报