AtCoder Beginner Contest 363 - VP记录

Preface

这场考的有点拉跨,D 题卡了好久,E 题因为乱剪枝 WA 了好多发。

总结一下,下次不会剪枝就不要乱剪枝,在 OI 比赛中,正确性比速度更重要

A - Piling Up

AtCoder 日爆导致半天登不上去。这道题还是看的洛谷上的题面,用洛谷 RMJ 交的。

点击查看代码
#include<cstdio>
using namespace std;

int main()
{
	int r; scanf("%d",&r);
	if(r<=99) printf("%d\n",100-r);
	else if(r<=199) printf("%d\n",200-r);
	else if(r<=299) printf("%d\n",300-r);
	else fprintf(stderr,"OUT OF RANGE\n");
	return 0;
}

B - Japanese Cursed Doll

还是登不上去,又是看的洛谷题面 + RMJ 提交(洛谷真好用)。

点击查看代码
#include<cstdio>
using namespace std;

const int N=105;
int n,t,p,a[N];

int main()
{
	scanf("%d%d%d",&n,&t,&p);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	int ans=0;
	while(true)
	{
		int cnt=0;
		for(int i=1;i<=n;i++)
			if(a[i]++>=t) cnt++;
		if(cnt>=p) break;
		ans++;
	}
	printf("%d\n",ans);
	return 0;
}

C - Avoid K Palindrome 2

初看这道题被吓了一跳,这题题面真的很像超难 DP,然而 \(2 \le N \le 10\),所以直接暴力枚举就好啦。

话说为什么每次 ABC 都会有这种暴力枚举的题啊?

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;

const int N=15;
int n,k;
char str[N];

int main()
{
	scanf("%d%d%s",&n,&k,str+1);
	sort(str+1,str+n+1);
	int ans=0;
	do
	{
		bool is_ok=true;
		for(int l=1;l+k-1<=n;l++)
		{
			int r=l+k-1;
			bool is_pal=true;
			for(int i=l;i<=l+r>>1;i++)
				if(str[i]!=str[r-i+l]) {is_pal=false; break;}
			if(is_pal) {is_ok=false; break;}
		}
		if(is_ok) ans++;
		next_permutation(str+1,str+n+1);
	}while(!is_sorted(str+1,str+n+1));
	printf("%d\n",ans);
	return 0;
}

D - Palindromic Number

卡得最久的一道题,我觉得应该评绿。

首先要找规律,先列出所有的回文数:

(如果左边有一列数字,不要管它,那是代码块行号)

0 1 2 3 4 5 6 7 8 9
11 22 33 44 55 66 77 88 99
101 111 121 131 141 151 161 171 181 191
202 212 222 232 ...

然后我大胆猜测答案的左边一半是 \(n - 10^{\lfloor \lg n \rfloor -1 }\),右边一半是左边一半反转过来,顶多再根据奇偶性判断一下是否合并其中一位。

然后成功的 WA 了。

我们不妨把这个表再列大一点:

1~10: 		0 1 2 3 4 5 6 7 8 9
11~19: 		11 22 33 44 55 66 77 88 99

20~29: 		101 111 121 131 141 151 161 171 181 191
30~39: 		202 212 222 232 242 252 262 272 282 292
...
90~99: 		808 818 828 838 848 858 868 878 888 898
100~109: 	909 919 929 939 949 959 969 979 989 999

110~119: 	1001 1111 1221 1331 1441 1551 1661 1771 1881 1991
120~129: 	2002 2112 2222 2332 2442 2552 2662 2772 2882 2992
...
190~199: 	9009 9119 9229 9339 9449 9559 9669 9779 9889 9999

发现什么没有?比如对于第 \(20 \sim 109\)\(110 \sim 200\) 个回文数,设其位数为 \(k\),那么它们的左边 \(\lceil \frac{k}{2} \rceil\) 位都在各自范围内依次递增,从 \(10\) 递增到 \(99\)

更进一步可以发现,\(20 \sim 109\)\(110 \sim 200\) 这两个范围内的回文数的唯一区别就是最中间有一位数是否合并。

根据以上规律,这个表就可以一直往后列下去:

1~10: 		0 1 2 3 4 5 6 7 8 9
11~19: 		11 22 33 44 55 66 77 88 99

20~109: 	101 111 ... 999
110~199: 	1001 1111 ... 9999

200~1099: 	10001 10101 ... 99999
1100~1999: 	100001 101101 ... 999999

具体来说,将所有的回文数分为几个组,每个组的范围是 \([2 \times 10^k , 20 \times 10^k]\),代表这个组内所有回文数的左边 \(\lceil \frac{k}{2} \rceil\) 位都在 \([10^k , 10 \times 10^k-1]\) 范围内依次递增。

而每个组内又可以分为两块,分别是 \([2 \times 10^k , 11 \times 10^k -1]\)\([11 \times 10^k , 20 \times 10^k - 1]\)。前一块的中间一位数要合并,后一块不合并。

根据上述结论分类讨论判断一下即可。

另外注意特判 \(n=1\) 的情况(这种情况跳出三界外,不在五行中,代码管不着它)。

#include<cstdio>
using namespace std;

long long n;

int main()
{
	scanf("%lld",&n);
	if(n==1)
	{
		printf("0\n");
		return 0;
	}
	int k=0;
	long long pow10=1;
	while(n>=2*pow10)
	{
		k++;
		pow10*=10;
	}
	k--,pow10/=10;
	long long mid=pow10*11;
	if(n<mid)
	{
		n-=pow10;
		printf("%lld",n);
		n/=10;
		while(n)
		{
			putchar('0'+n%10);
			n/=10;
		}
	}
	else
	{
		n-=pow10*10;
		printf("%lld",n);
		while(n)
		{
			putchar('0'+n%10);
			n/=10;
		}
	}
	return 0;
}

E - Sinking Land

赛事乱剪枝把代码剪挂了,下次不会剪枝就别乱剪,会出事的!

用 BFS 的方法,\(10^5\) 个队列来存坐标,队列 \(q_i\) 存的是有可能转移到高度为 \(i\) 的位置的坐标,然后设每一轮的水深是 \(r\),那么每次用 \(q_r\) 来进行 BFS 就可以了。

看起来似乎不太好理解,但是我考试的时候真是这么想的,虽然洛谷题解上还有更简单的做法。

#include<cstdio>
#include<queue>
using namespace std;

int read_u()
{
	int x=0; char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9')
	{
		x=(x<<3)+(x<<1)+(ch^'0');
		ch=getchar();
	}
	return x;
}

const int N=1005,M=1005,TI=1e5+5;
const int dx[10]={0,0,1,-1},dy[10]={1,-1,0,0};
int n,m,ti,a[N][M];
bool dead[N][M];

queue<pair<int,int>> q[TI];

inline bool check_pos(int x,int y){return x>=1&&x<=n && y>=1&&y<=m;}
int main()
{
	n=read_u(),m=read_u(),ti=read_u();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			a[i][j]=read_u();
	for(int i=1;i<=n;i++) q[1].push({i,0}),q[1].push({i,m+1});
	for(int i=1;i<=m;i++) q[1].push({0,i}),q[1].push({n+1,i});
	int alive=n*m;
	for(int r=1;r<=ti;r++)
	{
		while(!q[r].empty())
		{
			int x=q[r].front().first,y=q[r].front().second;
			q[r].pop();
			//不会剪枝就别TM乱剪!
			for(int i=0;i<4;i++)
			{
				int tx=x+dx[i],ty=y+dy[i];
				if(!check_pos(tx,ty)) continue;
				if(dead[tx][ty]) continue;
				if(a[tx][ty]>r)
				{
					q[a[tx][ty]].push({x,y});
					continue;
				}
				dead[tx][ty]=true;
				alive--;
				q[r].push({tx,ty});
			}
		}
		printf("%d\n",alive);
	}
	return 0;
}
posted @ 2024-11-02 11:19  Jerrycyx  阅读(9)  评论(0编辑  收藏  举报