台州学院第十六届大学生程序设计竞赛 (tzcoder.cn)
1001 : 'c'本就弱小
签到题,判断是否c + a > b且c + b > a即可
void solve()
{
double a, b, c;
scanf("%lf%lf%lf", &a, &b, &c);
if (c + a > b && c + b > a)
printf("Yes\n");
else
printf("No\n");
}
1002 : XOR!
小模拟,注意数据会爆int
#include<cstdio>
#include<cstring>
typedef long long LL;
int f[4];//用于判断符号
char op[] = { '<','>','=','!' };
bool check(LL x, LL y)
{
if (f[3])return x != y;
if (f[2])
{
if (f[1])return x >= y;
if (f[0])return x <= y;
return x == y;
}
if (f[1])return x > y;
return x < y;
}
void solve()
{
char c = getchar();
LL x = 0, y = 0, a = 0;
memset(f, 0, sizeof f);
while (c != '\n')
{
if (c == '+')
y += a, a = 0;
else if (c >= '0' && c <= '9')
a = (a << 1) + (a << 3) + c - '0';
else
{
x ^= a, a = 0;
if (c != '^')
{
for (int i = 0; i < 4; ++i)
if (c == op[i])f[i] = 1;
}
}
c = getchar();
}
//printf("%lld %lld\n", x, y + a);
if (check(x, y + a))printf("Yes\n");
else printf("No\n");
}
int main()
{
int T = 1;
scanf("%d", &T); getchar();
while (T--)
{
solve();
}
return 0;
}
1003 : 乐乐的强迫症
dp,状态表示及转移见代码
const int N = 2e2 + 10, M = 4e4 + 10, INF = 0x3f3f3f3f;
int dp[N][M];//dp[i][j]表示考虑到第i道题时,难度总分为j的最少做题数
void solve()
{
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
memset(dp, 0x3f, sizeof dp);
dp[0][a] = 0;//初始化
for (int i = 1, x; i <= n; ++i)
{
scanf("%d", &x);
for (int j = 0; j < x; ++j)
dp[i][j] = dp[i - 1][j];//不做这题,最少题数直接从上一题时难度分为j的最少题数转移过来
for (int j = x; j <= b; ++j)
dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - x] + 1);
//同上, 或者做了这题,最少题数为上一题时难度分为j - x的最少题数+1
}
if (dp[n][b] == INF)printf("-1\n");
else printf("%d\n", dp[n][b]);
}
1004 : maomeng的数学魔鬼训练
贪心的匹配每天看的书和完的游戏:所需时间最长的书匹配所需时间最短的游戏,所需时间次长的书匹配所需时间次短的游戏...
排序数组ai与bi后按上述方式安排每天的书与游戏,如有某天看书时长+游戏时长大于24小时则输出NO,若不存在该情况则输出YES
const int N = 1e3 + 10;
int a[N], b[N];
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i)
scanf("%d", &b[i]);
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
for (int i = 1, j = n; i <= n; ++i, --j)
{
if (a[i] + b[j] > 24)
{
printf("NO\n");
return;
}
}
printf("YES\n");
}
1005 : 回文自动机
不会矩阵快速幂,这里是打表找规律的做法
先把这个字符串想象成一个环,对字符串进行操作就相当于把字符串的开头位置右移了len个位置,所以在求方案数之前我们需要先求出所有可以作为回文串开头的位置。
处理环的一种简单方法是把字符串S后面再拼接一个S,这样从1到n的每个为止开始往后n个字符就是环中以i开头转一圈的字符串
判断回文可以使用字符串哈希,设(l, r)为字符串中从l位置到r为止的字符串,对当前字符串与将该字符串反转之后的字符串都做一遍字符串哈希,区间(l, r)与它在反转之后所在的区间(tx, ty)的哈希值相同则说明字符串(l, r)是一个回文串。对于本题我们需要枚举所有起点位置i判断字符串(i, i + n - 1)是否为回文串,若是则该位置可以作为回文串的开头位置
矩阵快速幂不会,但我会打表()
考虑一种O(K*N)的dp方式(显然过不了,只是拿它打个表),dp[i][j]表示第i次操作时,起点停留在j位置的方案数,dp[i][j]能从上一步操作的所有位置不在j位置的情况转移过来,然后我们用这种做法对样例打个表:
可以发现明显的规律:dp[i][2]到dp[i][n]的值全部相等,sum每次都是* (n - 1),dp[i][1]与其他值永远都相差1,且比其他值大1还是小1与i的奇偶性有关
当我们需要求出第k轮的dp值,设dp[k][1]为x,其他dp值为y,我们只需要用快速幂先求出sum[i],若k为奇数,则y = (sum + 1) / n, x = y - 1;若k为偶数,则y = (sum - 1) / n, x = y + 1(当然这里的除都是乘以乘法逆元,运算过程要注意取模)
之后对所有可以作为回文串开头的位置j的dp值累加即可dp[k][j] = (j == 1 ? x : y)
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const LL N = 2e5 + 10, MOD = 1e9 + 7, P = 131;
char ch[2 * N], revch[2 * N];
ULL p[2 * N], h[2 * N], revh[2 * N];
LL qpow(LL x, LL y)
{
x %= MOD;
if (y == 0)return 1;
if (y & 1)
return qpow(x * x, y >> 1) * x % MOD;
return qpow(x * x, y >> 1) % MOD;
}
LL inv(LL x)
{
return qpow(x, MOD - 2);
}
int revidx(int x, int n)
{
return 2 * n - x + 1;
}
ULL get_hash(ULL a[], int l, int r)
{
return a[r] - a[l - 1] * p[r - l + 1];
}
bool check(int l, int r, int n)
{
int tl = revidx(r, n), tr = revidx(l, n);
return get_hash(h, l, r) == get_hash(revh, tl, tr);
}
void solve()
{
LL k;
scanf("%s%lld", ch + 1, &k);
int n = strlen(ch + 1);
for (int i = n + 1; i <= 2 * n; ++i)
ch[i] = ch[i - n];
p[0] = 1;
for (int i = 1; i <= 2 * n; ++i)
{
p[i] = p[i - 1] * P;
revch[i] = ch[revidx(i, n)];
h[i] = h[i - 1] * P + ch[i];
revh[i] = revh[i - 1] * P + revch[i];
}
vector<int>v;
for (int i = 1; i <= n; ++i)
{
if (check(i, i + n - 1, n))
v.push_back(i);
}
//for (auto i : v)printf("%d ", i);
LL s = qpow(n - 1, k), x, y;
if (k & 1)y = (s + 1) * inv(n) % MOD, x = (y - 1 + MOD) % MOD;
else y = (s - 1 + MOD) % MOD * inv(n) % MOD, x = (y + 1) % MOD;
//printf("%lld %lld %lld\n", s, x, y);
LL ans = 0;
for (auto i : v)
{
if (i == 1)ans = (ans + x) % MOD;
else ans = (ans + y) % MOD;
}
printf("%lld\n", ans);
}
int main()
{
int T = 1;
//scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}
1006 : 狭路相逢勇者胜!
这种淘汰赛的结构差不多就是一颗满二叉树,具体就长这样:(偷来的图)
二叉树中的每一个节点i的父节点都是他除二向下取整(i/2)
如果我们把最下层的叶节点作为选手,则共有n名选手的情况下选手x对应的编号为n+x,而每次使节点编号除二使得选手到达他的父节点直到两名选手所在节点标号相同则两名选手相遇
void solve()
{
long long n, a, b, ans = 0;
scanf("%lld%lld%lld", &n, &a, &b);
a += n, b += n;
while (a != b) a /= 2, b /= 2, ++ans;
printf("%lld\n", ans);
}
1007 : "TZOJ"
算是挺经典的二位前缀和+二分题,题目灵感来源:E - Defect-free Squares (atcoder.jp)
首先我们先枚举正方形的左上角坐标(i,j),对于每个左上角的坐标我们可以二分符合条件的最小正方形边长(小的正方形满足条件则大的一定满足,边长满足二分性),而check一个矩形区域有没有"TZOJ"则可以通过做四个二位前缀和来分别统计'T', 'Z', 'O', 'J'四个字符的数量,然后查询这一块区域这存在这四个字符的数量是否均大于等于1,二分出最小的符合条件的边长d之后以(i,j)为左上角的正方形数量为min(n - i + 1, m - j + 1) - d + 1,由于会有以(i,j)为左上角不存在满足条件的正方形的情况,我们使二分的右初值r = min(n - i + 1, m - j + 1 ) + 1即可
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e3 + 10;
char ch[N][N], c[5] = "TZOJ";
int s[N][N][4];
int get_sum(int f, int ux, int uy, int vx, int vy)
{
return s[vx][vy][f] - s[vx][uy - 1][f] - s[ux - 1][vy][f] + s[ux - 1][uy - 1][f];
}
bool check(int ux, int uy, int vx, int vy)
{
for (int k = 0; k < 4; ++k)
{
if (get_sum(k, ux, uy, vx, vy) == 0)
return 0;
}
return 1;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
getchar();
for (int j = 1; j <= m; ++j)
ch[i][j] = getchar();
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
for (int k = 0; k < 4; ++k)
{
if (ch[i][j] == c[k])
++s[i][j][k];
s[i][j][k] += s[i - 1][j][k] + s[i][j - 1][k] - s[i - 1][j - 1][k];
}
}
}
long long ans = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
int maxd = min(n - i + 1, m - j + 1);
int l = 2, r = maxd + 1;
while (l < r)
{
int mid = l + r >> 1;
if (check(i, j, i + mid - 1, j + mid - 1))
r = mid;
else
l = mid + 1;
}
ans += maxd - l + 1;
}
}
printf("%d\n", ans);
return 0;
}
1008 : 佳加简减
易知要使得最长*****子序列的和最长的条件是使所有值相等。
那题目就变成了选择一个数x,求最小的,做过货仓选址的都直到这个x值是A数组的中位数,排序之后取出中位数,循环求出上述值即可。没做过就百度搜下
const int N = 1e5 + 10;
int a[N];
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]);
sort(a + 1, a + 1 + n);
LL ans = 0;
for (int i = 1; i <= n; ++i)
ans += abs(a[i] - a[(n + 1) / 2]);
printf("%lld\n", ans);
}
也可以选择枚举x,设当前小于等于x的数有t个,使x增大1能够使答案增大t, 减少n-t(具体变现为x与所有位置小于等于它的点距离都增大1,与所有位置大于它的点距离都减小1),预处理x=0时的答案s,然后从小到大枚举x并维护s即可
const int N = 1e5 + 10;
int a[N];
void solve()
{
int n, mx = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i]), mx = max(mx, a[i]);
sort(a + 1, a + 1 + n);
LL s = 0, ans = 1e10;
for (int i = 1; i <= n; ++i)
s += a[i];
for (int i = 1, j = 0; i <= mx; ++i)
{
s += j;
s -= n - j;
while (j < n && a[j + 1] <= i) ++j;
ans = min(ans, s);
}
printf("%lld\n", ans);
}
以上代码中的sort都可以用桶排序优化一个log
1009 : Auuuu的水题
DFS、BFS、暴力都能过
水的扩散只会发生在水源方块周围四个,对所有水源方块及新出现的水源方块周围四格都检查一下是否能被转化为水源方块,如能,则加入队列/dfs下去,这里放一个dfs做法
const int N = 5e2 + 10;
int n, m, dx[] = { 1,-1,0,0 }, dy[] = { 0,0,1,-1 }, f[N][N];
char ch[N][N];
int serch(int x, int y)
{
int res = 0;
for (int i = 0; i < 4; ++i)
{
int tx = x + dx[i], ty = y + dy[i];
if (ch[tx][ty] == '*')++res;
}
return res;
}
void dfs(int x, int y)
{
if (f[x][y])return;
f[x][y] = 1;
for (int i = 0; i < 4; ++i)
{
int tx = x + dx[i], ty = y + dy[i];
if (ch[tx][ty] == '.' && serch(tx, ty) >= 2)
{
ch[tx][ty] = '*';
dfs(tx, ty);
}
}
}
void solve()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
getchar();
for (int j = 1; j <= m; ++j)
ch[i][j] = getchar();
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
{
if (ch[i][j] == '*')
dfs(i, j);
}
}
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
putchar(ch[i][j]);
putchar('\n');
}
}
也可以全图扫一遍有没有变成水源的情况,有的话继续扫,没有就结束,这样应该最多需要扫2 * max(n, m)轮就能结束
1010 : Auuuu的粥游账号
普通的DFS题。数量可以直接算,n个数里选12个:C(n,12),也可以把结果存起来计数
int n, a[20];
long long c(int x, int y)
{
long long res = 1;
for (int i = 1; i <= y; ++i)
res *= x - i + 1;
for (int i = 1; i <= y; ++i)
res /= i;
return res;
}
void dfs(int step, int i)//第step个数,从i开始枚举
{
if (step > 12)
{
for (int i = 1; i <= 12; ++i)
{
if (i != 1)printf(" ");
printf("%d", a[i]);
}
printf("\n");
return;
}
for (; i <= n; ++i)
{
a[step] = i;
dfs(step + 1, i + 1);
}
}
void solve()
{
scanf("%d", &n);
printf("%lld\n", c(n, 12));
dfs(1, 1);
}
1011 : 上课不要讲话
存下所有Richadal可能出现的位置,再用二维数组标记所有Miako可能出现的位置,对所有可能出现Richadal的位置的周围8格搜一下是否可能存在Miako即可
const int N = 1e2 + 10;
int dx[] = { 0,0,1,-1,1,1,-1,-1 }, dy[] = { 1,-1,0,0,1,-1,1,-1 };
int sa[N], f[N][N];
void solve()
{
int n, m, a, b;
scanf("%d%d%d%d", &n, &m, &a, &b);
sa[0] = 1, f[n][m] = 1;
for (int i = 1; i <= a; ++i)
scanf("%d", &sa[i]);
for (int i = 1, x; i <= b; ++i)
{
scanf("%d", &x);
if (x > n * m)continue;//不知道为什么样例有17,反正加了应该问题
f[(x - 1) / m + 1][(x - 1) % m + 1] = 1;
}
for (int i = 0; i <= n; ++i)
{
int x = (sa[i] - 1) / m + 1, y = (sa[i] - 1) % m + 1;
for (int j = 0; j < 8; ++j)
{
int tx = x + dx[j], ty = y + dy[j];
if (f[tx][ty])
{
printf("Yes\n");
return;
}
}
}
printf("No\n");
}
1012 : Richadal和他的采矿车
结构体排序,因为题目很长并且需要写三个cmp所以多少有点若至(),需要注意完成采集任务的时间会爆int
#define _CRT_SECURE_NO_WARNINGS
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<array>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef long double LD;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const LL N = 2e3 + 10, MOD = 998244353, INF = 0x3f3f3f3f;
struct node
{
int id, cnt, s;
LL t;
//时间,编号,轮数,载矿量
}a[N];
bool cmpt(node x, node y)
{
if (x.t != y.t)
return x.t < y.t;
return x.id < y.id;
}
bool cmpid(node x, node y)
{
return x.id < y.id;
}
bool cmpcnt(node x, node y)
{
if (x.cnt != y.cnt)
return x.cnt < y.cnt;
return x.id < y.id;
}
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n / 2; ++i)
{
int x, y, z, s;
scanf("%d%d%d%d", &x, &y, &z, &s);
a[i].t = x + y + z;
a[i].id = i;
a[i].s = s;
}
for (int i = n / 2 + 1; i <= n; ++i)
{
int x, y, s;
scanf("%d%d%d", &x, &y, &s);
a[i].t = x + y;
a[i].id = i;
a[i].s = s;
}
int target, k;
scanf("%d%d", &target, &k);
for (int i = 1; i <= n; ++i)
{
// ↓向上取整的一种写法
a[i].cnt = (target + a[i].s - 1) / a[i].s;
a[i].t *= a[i].cnt;
}
sort(a + 1, a + 1 + n, cmpt);
printf("%d %d\n", a[k].id, a[k].cnt);
sort(a + 1, a + 1 + n, cmpid);
printf("%d %d\n", a[k].id, a[k].cnt);
sort(a + 1, a + 1 + n, cmpcnt);
printf("%d %d\n", a[k].id, a[k].cnt);
}
int main()
{
int T = 1;
scanf("%d", &T);
while (T--)
{
solve();
}
return 0;
}
1013 : yuyu与baby斗地主
模拟题,基本细心即可
把10, J, Q, K, A, 2分别转化为数字10, 11, 12, 13, 14, 15,把R, B转化为16写起来会简单不少
int cnta[20], cntb[20];
int input()//将输入的字符串转换为一个整数处理
{
char num[3], c[] = "1JQKA2";
scanf("%s", num);
for (int i = 0; i <= 5; ++i)
{
if (num[0] == c[i])
return 10 + i;
}
if (num[0] == 'R' || num[0] == 'B')
return 16;
return num[0] - '0';
}
void solve()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
++cnta[input()];
for (int i = 1; i <= m; ++i)
++cntb[input()];
cnta[16] *= 2, cntb[16] *= 2;
vector<int>sa, sb;
for (int i = 3; i <= 16; ++i)
{
if (cnta[i] == 4)
sa.push_back(i);
else if (cntb[i] == 4)
sb.push_back(i);
}
if (sa.size() > sb.size())
{
printf("yuyu\n");
return;
}
if (sa.size() < sb.size())
{
printf("baby\n");
return;
}
if (sa.empty())
{
printf("draw\n");
return;
}
int win = 0;
for (int i = 0; i < sa.size(); ++i)
{
if (sa[i] > sb[i])++win;
else --win;
}
if (win > 0)printf("yuyu\n");
else if (win < 0)printf("baby\n");
else if (sa.back() > sb.back())printf("yuyu\n");
else printf("baby\n");
}
因为我的头文件和typedef很长所以大多数题基本只放了核心代码...要看头文件什么的就找12题的代码