A题链接: https://ac.nowcoder.com/acm/contest/893/A
水题直接上代码:
#include<bits/stdc++.h>
using namespace std;
int n1, p1, s1, n2, p2, s2;
bool judge() {
if (n1 == n2 && p1 == p2)return s1 < s2;
if (n1 == n2)return p1 < p2;
return n1 > n2;
}
int main() {
scanf("%d%d%d", &n1, &p1, &s1);
scanf("%d%d%d", &n2, &p2, &s2);
if (judge())printf("1");
else if (n1 != n2 || p1 != p2 || s1 != s2)printf("2");
else printf("God");
printf("\n");
return 0;
}
B题链接: https://ac.nowcoder.com/acm/contest/893/B
解法:我是从最后一位不为0的数开始计数,第一个数num需要的操作数是10-num,再加上除以10这个操作,就是11-num,而之后的数有些许不同,因为前一位已经进了一位,所以总操作数是10-num。其中还要特判一下,例如10000000,这种操作只需要把最后的0去掉就好了,也就是说除掉后面的0,只有一位数,且这位数是1,那就不用操作。
代码:
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0;
char c = getchar();
while (c > '9' || c < '0') c = getchar();
while (c >= '0'&&c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
const int MAX = 1e5 + 10;
int T, N;
int main() {
T = read();
while (T--) {
N = read();
int ans = 0, flag = 0;
queue<int> q;
while (N) {
if (!flag && N % 10 == 0)ans++;
else q.push(N % 10), flag = 1;
N /= 10;
}
if (q.size() == 1 && q.front() == 1);
else ans += 11 - q.front();
q.pop();
while (!q.empty()) {
ans += 10 - q.front(); q.pop();
}
printf("%d\n", ans);
}
return 0;
}
C题链接: https://ac.nowcoder.com/acm/contest/893/C
题解: 直接扫一遍区间肯定会超时。这里需要知道:
a
3
a^3
a3 % 192 = (a % 192) * (a % 192) * (a % 192) % 192,所以这里情况无非就是b * b * b % 192 = 1(其中b = a % 192)。通过打表,在b∈[1, 191]之间,只有1满足,所以就只用找到区间[L,R]之间有没有数num,使得num % 192 == 1, 找到之后就可以看做等差数列求和就是了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T, l, r;
int main() {
scanf("%d", &T);
while (T--) {
ll ans = 0;
scanf("%d%d", &l, &r);
int p = 0;
for (int i = l; i <= r; i++)
if (i % 192 == 1) {
p = i;
break;
}
if (p == 0)printf("0\n");
else {
//公差为192, 首项为p, 项数为num
ll num = r / 192 - p / 192 + 1;
if (r % 192 == 0)num--;
printf("%lld\n", num * p + num * (num - 1) / 2 * 192);
}
}
return 0;
}
D题链接: https://ac.nowcoder.com/acm/contest/893/D
题解: 也算是水题,我一开始还没反应过来,考虑两堆合并,如果其中那个小的数c曾经是由两个数合并,即c = a + b (其中a < b), 那么答案相当于加上了2 * a + b, 但是如果你是一个一个合并,那答案就是加上a + b, 所以肯定是一个一个合并上去,所以只要去掉最大的那堆就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int T, N;
ll x;
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d", &N);
ll ans = 0;
ll maxx = 0;
for (int i = 1; i <= N; i++) {
scanf("%lld", &x);
ans += x;
maxx = max(maxx, x);
}
printf("%lld\n", ans - maxx);
}
return 0;
}
E题链接: https://ac.nowcoder.com/acm/contest/893/E
题解: 这题是后来补的,发现标答的做法非常牛逼(反正我想不出来)。标答是维护一个区间
[
L
,
R
]
[L, R]
[L,R],其中
R
R
R表示的是轮到这个人为止剩余最多的西瓜数(每个人都吃最少的),而
L
L
L表示剩余的最小西瓜数(每次都吃最多的),因此可以知道轮到当前这个人的时候还剩下
x
x
x个西瓜,
x
∈
[
L
,
R
]
x∈[L, R]
x∈[L,R]。那么在轮到lililalala的时候如果剩余的最大西瓜小于等于0,即
R
<
=
0
R<=0
R<=0,那么lililalala就铁定没有西瓜吃了,所以这种情况就是题意中的YES;在没轮到lililalala的时候,如果L<=0并且R<=0,也就是说没瓜可吃的情况下(这种情况一定是建立在上一次轮到lililalala有瓜吃的情况),那就是题意中的NO。
代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mp(x, y) make_pair(x, y)
#define fi first
#define se second
#define vi vector<int>
#define qi queue<int>
#define si stack<int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 1e5 + 10;
int T, N, M, a[MAX];
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &N, &M);//n人,m个西瓜
int pos, maxx = 0, L = M, R = M, flag = -1;
for (int i = 1; i <= N; i++) {
scanf("%d", &a[i]);
if (a[i] > maxx)maxx = a[i], pos = i;
}
while (flag == -1) {
for (int i = 1; i <= N; i++) {//维护[L,R]
if (i == pos && R <= 0) {//轮到lililalala没瓜吃
flag = 1;
break;
}
if (i != pos && L <= 0 && R <= 0) {//没轮到lililalala就已经没瓜吃了
flag = 0;
break;
}
L -= (i == pos ? maxx : 1);
R -= a[i];
}
}
if (flag)printf("YES\n");
else printf("NO\n");
}
return 0;
}
F题链接: https://ac.nowcoder.com/acm/contest/893/F
题意: 给一串只有01的字符串,一次操作能将0变成1,也能将1变成0,问在最多M次操作的情况下,得到的最大连续串(一串都是0或者一串都是1)。
题解: 解法是二分法,分的是最大连续串的长度,这里需要记录的就是一个前缀和,pre[i]记录的是i点(包括i点)之前1的个数,pre2[i]记录的是0的个数,所以二分的时候中间检查能否有pre[i + mid - 1] - pre[i - 1] <= M,也就是区间内1的个数小于M,或者0的个数小于M,如果有那就说明这个区间长度是可以找到的,就这样继续二分下去就是答案。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e5 + 10;
int pre[MAX], pre2[MAX], T, N, M;
char s[MAX];
int main() {
scanf("%d", &T);
while (T--) {
int ans = 0;
scanf("%d%d", &N, &M);
scanf("%s", s + 1);
for (int i = 1; i <= N; i++) {
int num = (int)(s[i] - '0');
pre[i] = pre[i - 1], pre2[i] = pre2[i - 1];
if (num == 0)pre[i]++;
else pre2[i]++;
}
int l = 1, r = N, mid;//二分区间长度
while (l <= r) {
mid = l + r >> 1;
int flag = 0;
for (int i = 1; i + mid - 1 <= N; i++)
if (pre[i + mid - 1] - pre[i - 1] <= M) {
flag = 1;
break;
}
if (flag == 0) {
for (int i = 1; i + mid - 1 <= N; i++)
if (pre2[i + mid - 1] - pre2[i - 1] <= M) {
flag = 1;
break;
}
}
if (flag == 0)r = mid - 1;//说明长度大了
else l = mid + 1, ans = max(ans, mid);
}
printf("%d\n", ans);
}
return 0;
}
H题链接: https://ac.nowcoder.com/acm/contest/893/H
题解: 这题也是补的。标答的意思是分组背包,先求出当天产生i点怒气的最小上线时间,然后以怒气值为背包空间,当天的不同怒气产生的耗时t[i]作为物品。
代码:
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mp(x, y) make_pair(x, y)
#define fi first
#define se second
#define vi vector<int>
#define qi queue<int>
#define si stack<int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 210;
int T, N, M, K;
char s[MAX];
int t[MAX], pre[MAX];//t[i]为当前这天产生i点怒气的最小耗时
int DP[MAX][MAX];//DP[i][j]为第i天产生j点怒气的最小耗时
//状态转移方程:DP[i][j] = min(DP[i][j], DP[i - 1][j - k] + t[k])
int main() {
scanf("%d", &T);
while (T--) {
memset(DP, 0x3f3f3f, sizeof(DP));
scanf("%d%d%d", &N, &M, &K);
DP[0][0] = 0;
for(int i = 1; i <= N; i++) {//预处理
memset(t, 0x3f3f3f, sizeof(t));
memset(pre, 0, sizeof(pre));
scanf("%s", s + 1);
int num = 0;//记录当天最多产生多少怒气
for (int j = 1; j <= M; j++) {
pre[j] = pre[j - 1];//pre记录到当前位置为止有多少个‘1’
if (s[j] == '1')num++, pre[j]++;
}
for (int l = 1; l <= M; l++)
for (int r = l; r <= M; r++) {
int n = pre[r] - pre[l - 1];//区间[l, r]内‘1’的个数,则num - n为产生怒气值
t[num - n] = min(t[num - n], r - l + 1);
}
t[num] = min(t[num], 0);
for (int j = K; j >= 0; j--)
for (int k = 0; k <= num; k++)
if (j >= k)DP[i][j] = min(DP[i][j], DP[i - 1][j - k] + t[k]);
}
int ans = INF;
for (int i = 0; i <= K; i++)
ans = min(ans, DP[N][i]);
printf("%d\n", ans);
}
return 0;
}