A
给定一个字符串,找出这个字符串中出现了多少次“tjmts”
字符串最大长度1e4
长度短,可暴力。或者kmp
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAX = 10009;
int main()
{
int t;
int nt[MAX];
char s[MAX], w[MAX] = "tjmts";
int i, j, len1;
int lenw = strlen(w);
i = 0;
j = -1;
nt[0] = -1;
while (i < lenw) {
if (j == -1 || w[i] == w[j]) {
nt[++ i] = ++ j;
} else {
j = nt[j];
}
}
scanf("%d",&t);
while (t --) {
int len;
scanf("%d %s", &len, s);
int ans = 0;
// int len = strlen(s);
int i = 0; j = 0;
while (i < len) {
if (s[i] == w[j] || j == -1) {
i ++; j ++;
} else {
j = nt[j];
}
if (j == lenw) {
ans ++;
j = nt[j];
}
}
printf("%d\n", ans);
}
return 0;
}
D
有一些物品,一共n种,k个人分,要求每个人分到的物品必须来自同一类,并且,这k个人分到的物品数必须是相同的,求每个人最多能分到多少物品
n最大是1000,一种物品最多1e8个,k最大1e9
没有什么直接找到答案的好办法,但对于给定的一个值容易判断是否是一个合法的值,并且如果k个人每人可以得到m个,那么m-1一定是一个合法的值,如果p是一个不合法的值,那么p+1一定不合法。n的值比较小,验证一个值是否合法的代价不大,所以,可以二分答案,答案就是所有合法值中的最大者。
坑点是答案可能是0,二分时中值如果是0,判断时就会出现除0,特判一下就可以了。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
using namespace std;
int n, k, a[1010];
bool ok(int x)
{
int cnt = 0;
for (int i = 1; i <= n; i ++) {
cnt += a[i] / x;
if (cnt >= k)
return true;
}
return false;
}
int bin(int l, int r)
{
int mid, ans;
while (l <= r)
{
mid = (l + r) >> 1;
if (mid == 0) {
ans = 0;
l = 1;
continue;
}
if (ok(mid)) {
l = mid + 1;
ans = mid;
} else {
r = mid - 1;
}
}
return ans;
}
int main()
{
int t;
scanf("%d", &t);
while (t --) {
scanf("%d%d",&n,&k);
for (int i = 1 ; i <= n; i ++) {
scanf("%d", &a[i]);
}
printf("%d\n", bin(0, 1e8 + 1));
}
return 0;
}
F
简单博弈题,对于一个给定的数,你可以对这个数做减法,要求减数必须是当前这个数的非零的某一位,先把数字减到0的胜。
对于所有非零个位数,可以一步减到0,是必胜态,10只能减到9,是必败态,而11 ~ 19都可以一步到达10,所以都是必胜态,同理20是必败态……故末位为0是必败态,非0是必胜态。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
using namespace std;
int main()
{
int t, n;
scanf("%d", &t);
while (t --) {
scanf("%d", &n);
if (n % 10 == 0) {
puts("2");
} else {
puts("1");
}
}
return 0;
}
B
题意很简单,一个长度为n的序列,要求找出一种排序,这个序列的最大上升子序列长度为m,最大下降子序列为k,如果存在多种,输出字典序最小的一种,如果不存在,则输出一个空行。
难度就在字典序最小上。可以这样构造,先从1开始考虑,先排一段上升序列,然后再排下降序列(下降序列可以排列成多个块),这样可以保证字典序最小,但同时排序必须合法。
例如
n = 48 m = 7 k = 17
1 2 3 4 14 13 12 11 10 9 8 7 6 5 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 48 47
46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
下降的序列可以有多个,观察可知,k的值就是下降的序列中最长序列的长度,m就是上升序列长度+下降序列个数。
因为n最大为50,所以可以枚举开始上升序列的长度,则对应长度需要固定数量的下降序列,试填之,如果找到合法序列,输出,找不到则说明不存在。
注意细节问题,比如说上升序列长度可以为0,特殊情况的特判等等
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
using namespace std;
int main()
{
int t, n, m, k, a[110];
scanf("%d", &t);
while (t --) {
scanf("%d%d%d", &n, &m, &k);
if (k <= 0 || m <= 0) {
printf("\n");
continue;
}
if (k > n || m > n) {
printf("\n");
continue;
}
if (n == 3 && (k == 1 && m == 2 || m == 1 && k == 2)) {
printf("\n");
continue;
}
if (n == 1) {
printf("1\n");
continue;
}
if (n == 3 && k == 1 && m == 3) {
puts("1 2 3");
continue;
}
if (n == 3 && k == 3 && m == 1) {
puts("3 2 1");
continue;
}
int fflag = 0;
for (int ii = 1; ii <= m ; ii ++)
{
int cnt = 0;
for (int i = 1; i <= m - ii; i ++) {
a[++ cnt] = i;
}
int sum = cnt;
// printf("%d : %d \n", ii, cnt);
if (n - sum < k) {
continue;
}
int loc = n;
int time = 1;
int key ;
while (k < n - cnt) {
int i;
for (i = n - time * k + 1; i <= n - (time - 1) * k; i ++) {
a[i] = n + n - i - time * k + 1 - (time - 1) * k;
++ cnt;
}
loc = n - time * k;
-- i;
key = 2 * n - i - time * k - (time - 1) * k;
++ time;
}
for (int i = sum + 1; i <= loc; i ++) {
a[i] = key --;
}
++ key;
if (sum == 0) a[sum] = 0;
if (a[sum] + 1 != key || time != ii) {
// printf("%d %d\n", time, ii);
// printf("%d %d\n", a[sum], key);
// printf("%d\n", sum);
continue;
}
fflag = 1;
printf("%d", a[1]);
for (int i = 2; i <= n ; i ++) {
printf(" %d", a[i]);
}
printf("\n");
break;
}
if (!fflag) {
printf("\n");
}
}
return 0;
}
G
这个题现场没做出来,没读懂题,也没带字典,后来知道题意之后发现原来不难。题意是对于一个给定的矩形和给定的P点,按顺时针过矩形的顶点和P点做直线,过 顺时针序的上一个顶点过这条直线的垂线,问如此做出的四条垂线是否交与一点。
题目规定P点不与矩形顶点垂直,所以不存在平行的情况。先求出两条垂线的交点,再判断这个点在不在另外两条垂线上即可。