Kick Start Round A 2022

Speed Typing

给两个字符串 I I I P P P,问能否通过删除串 P P P 中的字符,使得 P P P 变成 I I I,如果能输出删除的字符数,不能输出 IMPOSSIBLE

模拟题。将 I I I P P P 从左到右进行比对,如果不相符,则删去 P P P 中该字符,直到结束后,判断是否相同即可。 O ( n ) O(n) O(n)

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
char I[100010], P[100010];
int main(){
	cin >> T;
	for(int t = 1; t <= T; ++t){
		cout << "Case #" << t << ": ";
		cin >> I >> P;
		int i = strlen(I), p = strlen(P);
		int ii = 0, cnt = 0;
		for(int pp = 0; pp < p; ++pp){
			if(ii == i) ++cnt;
			else if(I[ii] == P[pp]) ++ii;
			else ++cnt;
		}
		if(ii == i) cout << cnt << endl;
		else puts("IMPOSSIBLE");
	}
	return 0;
}

Challenge Nine

给定一个数 N N N,在其中恰好插入一个数使得其是 9 9 9 的倍数,如果有多个答案,输出最小的。
N ≤ 1 0 123456 N\le 10^{123456} N10123456

如果一个数是 9 9 9 的倍数,则各个位数加起来一定是 9 9 9 的倍数,于是可以得到要插入的数 x x x。由于答案要求最小,所以采用贪心的策略,从高位到低位找到第一个小于 x x x 的数, x x x 插入到该数之前即可,注意不能有前导 0 0 0

由于我一开始没看到要求恰好插入一个数,于是 0 0 0 的时候直接输出原串,WA了一发。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
char N[1000010];
int main(){
	cin >> T;
	for(int t = 1; t <= T; ++t){
		cout << "Case #" << t << ": ";
		cin >> N;
		int sum = 0;
		for(int i = 0; N[i]; ++i){
			sum += N[i] - '0';
		}
		sum %= 9;
		sum = 9 - sum;
		sum %= 9;
		bool flag = true;
		for(int i = 0; N[i]; ++i){
			if(flag == true && N[i] - '0' > sum){
				if(sum == 0 && i == 0){}
				else {
					putchar(sum + '0');
					flag = false;
				}
			}
			putchar(N[i]);
		}
		if(flag == true) putchar(sum + '0');
		puts("");
	}
	return 0;
}

Palindrome Free Strings

一开始第一眼没想出来,还以为是个丧病题。

给一个由 0,1,? 构成的字符串 S S S,你需要将 ? 替换为 01,使得该字符串不包含长度大于等于 5 5 5 的回文串。

不包含长度大于等于 5 5 5 的回文串等价于不包含长度等于 5 5 5 6 6 6 的回文串。

所以我们可以从左到右枚举 ? 应该被替换成什么,但是只需要记录下当前位置往前 6 6 6 个字符是什么,如果往前 6 6 6 个字符可以构成长度等于 5 5 5 6 6 6 的回文串,则不合法。

时间复杂度 O ( 2 6 ∣ S ∣ ) O(2^6|S|) O(26S)

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
int n[20];
bool p5[1 << 5], p6[1 << 6];
void pre(){
	int w6[] = {33, 18, 12};
	int w5[] = {17, 10, 4};
	for(int i = 0; i < (1 << 3); ++i){
		int u6 = 0, u5 = 0;
		for(int j = 0; j < 3; ++j){
			if((i >> j) & 1){
				u6 += w6[j];
				u5 += w5[j];
			}
		}
		p5[u5] = 1;
		p6[u6] = 1;
	}
}
bool f[50010][1 << 6];
char s[50010];
int trim(int val, int v){
	val = (val << 1) | v;
	val &= (1 << 6) - 1;
	return val;
}
int main(){
	pre();
	cin >> T;
	for(int t = 1; t <= T; ++t){
		cout << "Case #" << t << ": ";
		memset(f, 0, sizeof(f));
		int n; cin >> n;
		cin >> s;
		f[0][0] = 1;
		for(int i = 0; s[i]; ++i){
			for(int j = 0; j < (1 << 6); ++j) if(f[i][j]){
				if(s[i] == '1' || s[i] == '?'){
					int t = trim(j, 1);
					bool flag = true;
					if(i >= 4 && p5[t & ((1 << 5) - 1)] == 1) flag = false;
					if(i >= 5 && p6[t] == 1) flag = false;
					if(flag) f[i + 1][t] = 1;
				}
				if(s[i] == '0' || s[i] == '?'){
					int t = trim(j, 0);
					bool flag = true;
					if(i >= 4 && p5[t & ((1 << 5) - 1)] == 1) flag = false;
					if(i >= 5 && p6[t] == 1) flag = false;
					if(flag) f[i + 1][t] = 1;
				}

			}
		}
		int l = strlen(s);
		bool flag = true;
		for(int j = 0; j < (1 << 6); ++j) if(f[l][j] == 1){
			puts("POSSIBLE");
			flag = false;
			break;
		}
		if(flag) puts("IMPOSSIBLE");
	}
	return 0;
}

Interesting String

当时是先做的这题,才做的前一题,因为第 3 3 3 题没看出来咋做一开始,然后还看到一堆人过人这题。瞅了一眼发现好像是数位 DP 就来试试了。

[ A , B ] [A,B] [A,B] 间有多少个整数满足,其各个位数之积可以被各个位数之和给整除。
1 ≤ A ≤ B ≤ 1 0 12 1\le A\le B\le10^{12} 1AB1012

[ A , B ] [A,B] [A,B] 计数可以转换为 [ 1 , B ] − [ 1 , A ] [1,B]-[1,A] [1,B][1,A],转成两个前缀和相减。

像这种对数的各个位数进行操作,通常可以想到数位 DP。

假设已知各个位数之和为 s s s,则就是要判断是否各个位数之积 p p p s s s 是否为 0 0 0。根据 B B B 的大小可知, s s s 不会超过 110 110 110 比较小。

所以我们枚举 s s s,然后利用数位DP来求出有多少个满足和为 s s s,积在模 p p p 意义下为 0 0 0 的数。

注意前导 0 0 0 要特殊考虑。

这题好像我的做法有点儿卡常,所以需要剪剪枝。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
int T;
int n[20];
long long f[20][110][110][2][2]; //1 top, 1 leading zero
long long calc(long long v){
	if(v == 0) return 0;
	int p = 0;
	long long tmp = v;
	while(tmp > 0){
		n[p] = tmp % 10;
		++p;
		tmp /= 10;
	}
	long long ans = 0;
	for(int i = 1; i < 110; ++i){
		memset(f, 0, sizeof(f));
		f[p][0][1 % i][1][1] = 1;
		for(int id = p; id > 0; id--){
			for(int sum = 0; sum <= i; ++sum){
				for(int prod = 0; prod < i; ++prod){
					if(!f[id][sum][prod][0][0] && !f[id][sum][prod][1][0] && !f[id][sum][prod][0][1] && !f[id][sum][prod][1][1]) continue;
					if(0 < n[id - 1]){
						f[id - 1][sum][0][0][0] += f[id][sum][prod][1][0];
						f[id - 1][sum][prod][0][1] += f[id][sum][prod][1][1];
					}
					if(0 == n[id - 1]){
						f[id - 1][sum][0][1][0] += f[id][sum][prod][1][0];
						f[id - 1][sum][prod][1][1] += f[id][sum][prod][1][1];
					}
					f[id - 1][sum][prod][0][1] += f[id][sum][prod][0][1];
					f[id - 1][sum][0][0][0] += f[id][sum][prod][0][0];
					for(int num = 1; num < 10; ++num){
						if(num < n[id - 1]){
							f[id - 1][sum + num][prod * num % i][0][0] += f[id][sum][prod][1][1] +
								f[id][sum][prod][1][0];
						}
						if(num == n[id - 1]){
							f[id - 1][sum + num][prod * num % i][1][0] += f[id][sum][prod][1][1] +
								f[id][sum][prod][1][0];
						}
						f[id - 1][sum + num][prod * num % i][0][0] += f[id][sum][prod][0][1] +
								f[id][sum][prod][0][0];
					}
				}
			}
		}
		ans += f[0][i][0][0][0] + f[0][i][0][1][0] + f[0][i][0][0][1] + f[0][i][0][1][1];
	}
	return ans;
}
int main(){
	cin >> T;
	for(int t = 1; t <= T; ++t){
		cout << "Case #" << t << ": ";
		long long A, B;
		cin >> A >> B;
		cout << calc(B) - calc(A - 1) << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值