题解
A. Floor Number
题意:
Petya的公寓号是n,公寓楼号是这样安排的,第一层是1,2号,第二层是3到 (x + 2), 第三层是(x + 3)到(2x + 2)…依次类推。问Petya住第几层。
思路:
这里简单的谈一下快速向上取整的思路,x / y 快速向上取整,在不用库函数的情况下,ceil(x / y) = (x - 1) / y + 1
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
int n, x;
scanf("%d%d", &n, &x);
if (n <= 2) {
puts("1");
return;
}
int ans = (n - 2 - 1) / x + 1;
printf("%d\n", ans + 1);
}
int main() {
int t;
scanf("%d", &t);
for (int i = 0; i < t; ++i) {
solve();
}
return 0;
}
B. Symmetric Matrix
题意
Masha有n种类型的2 * 2图块,每个类型有无限多个,问是否可以构成一个m * m的图块,这个图块关于主对角线对称。任意两个图块不能有覆盖。
思路
当m为奇数时,肯定不能构成。
当m为偶数时,关于主对角线对称,我们只需保证有一个块的[1][0]和[0][1]相同,就能构成。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 150;
int a[3][3];
void solve() {
int n, m;
bool f = false;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) {
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
scanf("%d", &a[i][j]);
}
}
if (a[1][0] == a[0][1]) {
f = true;
}
}
if ((m & 1) || !f) {
puts("NO");
} else {
puts("YES");
}
}
int main() {
int t;
scanf("%d", &t);
for (int i = 0; i < t; ++i) {
solve();
}
return 0;
}
C. Increase and Copy
题意
最开始数组里面有一个元素为1,即a = {1}
你每一步可以完成以下操作:
1.将数组中的任意一个元素复制到末尾。
2.将数组中的任意一个元素+1.
问将数组元素和变为n,最少需要多少步。
思路
思路:
很显然我们可以先将第一个加x,然后再复制y次,是得到最优(次数最少)的n。
令m = x + y;
n = (x + 1) * ( y + 1); —> 式子a
两个自变量x和y,一个因变量m。
由式子a减少自变量的数目,y = n / (x + 1) - 1 -->式子b;
然后我们只需要枚举一个变量x即可。
这里需要注意,除数,我们需要 ceil(X/Y) = (X - 1)/Y + 1 或者 ceil(X/Y) = (X + Y - 1) / Y。
式子b变为 y = (n - 1) / (x + 1);
AC代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200000 + 50;
const int mod = 1e9 + 7;
typedef long long ll;
void solve() {
ll n;
scanf("%lld", &n);
if (n == 1) {
puts("0");
return;
}
ll i = 1, ans = 1e9;
while (i * i <= n) {
ans = min(ans, (n - 1) / (i + 1) + i);
i++;
}
printf("%lld\n", ans);
}
int main() {
int t;
scanf("%d", &t);
for (int i = 0; i < t; ++i) {
solve();
}
return 0;
}
D. Non-zero Segments
题意
数组里有n个整数,Kolya 不喜欢0, 所以不能连续的子序列和等于0,你可以插入一些无穷大进去,使得连续子序列和不等于0。问最少需要插入多少个无穷大?
思路
算数组中有多少个连续子序列和为0的个数,我们都能想到用map或者set来求,但是这个题存在满足子序列和为0的区间覆盖。例如 -2, -2 4, -2.
[1,3]和[2,4]都满足,但是只需要插入一个即可,在[2,3]之间插入一个无穷大即可,多枚举几个这样的重复区间,我们会得到一个规律,只要区间重复我们就可以在覆盖区间里面插入一个无穷大即可,那么还会有这样一种情况,-2, -2, 4, -2, -2,我们不考虑重复区间,[1,3]和[3,5]这两个区间满足,我们用set来处理这个,当查到当前位置有序列为0的,记录答案,删除原来的记录,插入当前的sum和前一个sum(处理一个区间的左端点和另一个区间的右端点覆盖,仔细品)。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200000 + 50;
void solve() {
int n, x, ans = 0;
scanf("%d", &n);
ll sum = 0;
set<ll> st;
st.insert(sum);
for (int i = 0; i < n; ++i) {
scanf("%d", &x);
sum += x;
if (st.count(sum)) {
ans++;
st.clear();
}
st.insert(sum);
st.insert(sum - x);
}
printf("%d\n", ans);
}
int main() {
//int t;
//scanf("%d", &t);
//for (int i = 0; i < t; ++i) {
solve();
//}
return 0;
}
E. Rock, Paper, Scissors
题意
Alice and Bob在玩石头剪刀布的游戏,Alice要出a1,a2,a3个石头,剪刀,布,Bob要出b1,b2,b3。a1+a2+a3 = b1 + b2 + b3 = n;
求Alice 能赢的最小回合数;Alice能赢的最大回合数。
思路
最大回合数很好求,让alice能赢的都赢(看代码),
最少回合数,Alice 的出石头的数目,先用Bob的布消去,如果没消去完,再用Bob的石头消去,这样Alice出石头赢得局数变得最小,出剪刀和布也是如此。最后再求Alice能赢多少局。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200000 + 50;
int n, a[4], b[4];
void go(int x, int y, int z) {
if (a[x] < b[y]) {
a[x] = 0;
b[y] -= a[x];
} else {
a[x] -= b[y];
if (a[x] > b[z]) {
a[x] -= b[z];
b[z] = 0;
} else {
a[x] = 0;
b[z] -= a[x];
}
}
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= 3; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= 3; ++i) scanf("%d", &b[i]);
int mx = min(a[1], b[2]) + min(a[2], b[3]) + min(a[3], b[1]);
int mi = mx;
go(1, 3, 1);
go(2, 1, 2);
go(3, 2, 3);
mi = min(mi, min(a[1], b[2]) + min(a[2], b[3]) + min(a[3], b[1]));
printf("%d %d\n", mi, mx);
}
int main() {
//int t;
//scanf("%d", &t);
//for (int i = 0; i < t; ++i) {
solve();
//}
return 0;
}
F. Number of Subsequences
题意
给出一个字符串,字符串包括’a’,‘b’,‘c’,’?‘这四个字符,’?'可代替abc中的任意一个,问这个字符串构成子序列为"abc"的总个数。
思路
动态规划问题,后面的状态是由前面的状态推出来的。
设 dp[i][0] 代表当前序列有多少个,dp[i][1],表示有多少个a,dp[i][2],表示有多少个ab,dp[i][3] 表示有多少个abc
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200000 + 50;
const int mod = 1e9 + 7;
char s[maxn];
ll dp[maxn][4]; // dp[i][0] 代表有序列?,dp[i][1],表示有多少个a,dp[i][2],表示有多少个ab序列,dp[i][3] 表示有多少个abc序列
// ?对序列a有贡献,a对序列ab有贡献,ab对序列abc有贡献。贡献是累计的。
void solve() {
int n;
scanf("%d%s", &n, &s);
dp[0][0] = 1;
for (int i = 0; i < n; ++i) {
//当前状态由上一个状态推过来
for (int j = 0; j <= 3; ++j) {
dp[i + 1][j] = dp[i][j];
}
if (s[i] == 'a') {
dp[i + 1][1] = dp[i + 1][1] + dp[i][0] % mod;
} else if (s[i] == 'b') {
dp[i + 1][2] = dp[i + 1][2] + dp[i][1] % mod;
} else if (s[i] == 'c') {
dp[i + 1][3] = dp[i + 1][3] + dp[i][2] % mod;
} else {
dp[i + 1][0] = dp[i][0] * 3 % mod;
for (int j = 1; j <= 3; ++j) {
dp[i + 1][j] = (dp[i][j] * 3 % mod + dp[i][j - 1] % mod) % mod;
}
}
}
printf("%lld\n", dp[n][3] % mod);
}
int main() {
//int t;
//scanf("%d", &t);
//for (int i = 0; i < t; ++i) {
solve();
//}
return 0;
}