AtCoder Beginner Contest 363 - VP記錄

Jerrycyx發表於2024-11-02

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;
}

相關文章