2017 ACM Amman Collegiate Programming Contest 题解

时间:2022-11-04 07:23:50

题目链接

A - Watching TV

模拟。统计一下哪个数字最多即可。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10; int T, n;
char s[maxn];
int a[maxn]; int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
memset(a, 0, sizeof a);
for(int i = 1; i <= n; i ++) {
int x;
scanf("%s%d", s, &x);
a[x] ++;
}
int mx = 0;
for(int i = 11111; i <= 99999; i ++) {
mx = max(mx, a[i]);
}
for(int i = 11111; i <= 99999; i ++) {
if(a[i] == mx) {
printf("%d\n", i);
break;
}
}
}
return 0;
}

B - Longest Prefix

模拟。一个串能乱变,一个串不能动,只要统计能变的那个串每个字母有几个即可,到不能动的串上来消耗。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10; int T, n;
char s[maxn], t[maxn];
int cnt[500]; int main() {
scanf("%d", &T);
while(T --) {
memset(cnt, 0, sizeof cnt);
scanf("%s%s", s, t);
int lens = strlen(s);
int lent = strlen(t); for(int i = 0; t[i]; i ++) {
cnt[t[i]] ++;
} int ans = -1;
for(int i = 0; i < lens; i ++) {
if(cnt[s[i]] == 0) break;
ans = i;
cnt[s[i]] --;
}
printf("%d\n", ans + 1); }
return 0;
}

C - Lunch Break

水题。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10; int T, n;
int a, b, c; int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d%d", &a, &b, &c);
if(a < b && a < c) {
printf("First\n");
}
if(b < a && b < c) {
printf("Second\n");
}
if(c < a && c < b) {
printf("Third\n");
} }
return 0;
}

D - Counting Paths

组合数。通过分析可以发现答案为$2*C_{n - 1}^m$。

#include <iostream>
#include <string.h>
#include <stdio.h> using namespace std;
typedef long long LL; LL n,m;
LL p = 1e9 + 7; const int maxn = 1e5 + 10;
LL f[maxn]; //******************************
//返回d=gcd(a,b);和对应于等式ax+by=d中的x,y
long long extend_gcd(long long a,long long b,long long &x,long long &y)
{
if(a==0&&b==0) return -1;//无最大公约数
if(b==0){x=1;y=0;return a;}
long long d=extend_gcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
//*********求逆元素*******************
//ax = 1(mod n)
long long mod_reverse(long long a,long long n)
{
long long x,y;
long long d=extend_gcd(a,n,x,y);
if(d==1) return (x%n+n)%n;
else return -1;
} LL C(LL n, LL m)
{
long long A = f[n];
long long B = f[n - m] * f[m] % p;
long long C = mod_reverse(B, p);
return A * C % p;
} int main()
{
int T;
f[0] = 1;
for(long long i = 1; i < maxn; i ++) {
f[i] = f[i - 1] * i % p;
}
scanf("%d", &T);
while(T--)
{
scanf("%lld%lld", &n, &m);
if(n == 0) {
printf("0\n");
continue;
}
printf("%lld\n", C(n - 1, m) * 2 % p);
}
return 0;
}

E - Car Factory

水题。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10; int T, n;
char s[maxn], t[maxn];
int cnt[500]; int main() {
scanf("%d", &T);
while(T --) {
long long a, b;
cin >> a >> b;
cout << a + b - 1 << endl; }
return 0;
}

F - Cooking Time

贪心,线段树。如果满了,每次应该扔掉最晚用的那个。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10;
int T, n, k;
int a[maxn], b[maxn], nx[maxn], pos[maxn];
int s[4 * maxn];
int f[maxn]; int lsh(int x) {
int L = 1, R = n;
while(L <= R) {
int mid = (L + R) / 2;
if(b[mid] > x) R = mid - 1;
else if(b[mid] < x) L = mid + 1;
else return mid;
}
return 0;
} void build(int l, int r, int rt) {
s[rt] = 0;
if(l == r) return;
int mid = (l + r) / 2;
build(l, mid, 2 * rt);
build(mid + 1, r, 2 * rt + 1);
} void update(int p, int val, int l, int r, int rt) {
if(l == r) {
s[rt] = val;
return;
}
int mid = (l + r) / 2;
if(p <= mid) update(p, val, l, mid, 2 * rt);
else update(p, val, mid + 1, r, 2 * rt + 1);
s[rt] = max(s[2 * rt], s[2 * rt + 1]);
} int work(int l, int r, int rt) {
if(l == r) return l;
int mid = (l + r) / 2;
if(s[2 * rt] > s[2 * rt + 1]) return work(l, mid, 2 * rt);
else return work(mid + 1, r, 2 * rt + 1);
} int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
b[i] = a[i];
f[i] = 0;
pos[i] = n + 1;
}
sort(b + 1, b + 1 + n);
for(int i = 1; i <= n; i ++) {
a[i] = lsh(a[i]);
}
for(int i = n; i >= 1; i --) {
nx[i] = pos[a[i]];
pos[a[i]] = i;
}
build(1, n, 1);
int ans = 0;
int now = 0;
for(int i = 1; i <= n; i ++) {
if(f[a[i]]) {
update(a[i], nx[i], 1, n, 1);
continue;
}
ans ++;
if(now < k) {
f[a[i]] = 1;
now ++;
update(a[i], nx[i], 1, n, 1);
} else {
int del = work(1, n, 1);
update(del, 0, 1, n, 1);
update(a[i], nx[i], 1, n, 1);
f[del] = 0;
f[a[i]] = 1;
}
}
printf("%d\n", ans);
}
return 0;
}

G - Super Subarray

区间和要能被区间内每个数都整除,就是区间和要能被区间的最小公倍数整除,因此处理出区间的和以及最小公倍数即可,注意爆long long。

#include <bits/stdc++.h>
using namespace std; const int maxn = 2000 + 10;
int T;
int n, k;
long long a[maxn];
long long sum[maxn][maxn];
long long lcm[maxn][maxn];
long long limit = 2000LL * 1e9; long long gcd(long long a, long long b) {
if(b == 0) return a;
return gcd(b, a % b);
} long long LCM(long long a, long long b) {
long long A = a / gcd(a, b);
long long B = b; if(A > limit / B) return -1;
return A * B;
} int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%lld", &a[i]);
}
int ans = 0;
for(int i = 1; i <= n; i ++) {
for(int j = i; j <= n; j ++) {
sum[i][j] = sum[i][j - 1] + a[j];
if(j == i) lcm[i][j] = a[j];
else lcm[i][j] = LCM(a[j], lcm[i][j - 1]);
if(lcm[i][j] > limit || lcm[i][j] == -1) break;
if(sum[i][j] % lcm[i][j] == 0) ans ++;
}
}
printf("%d\n", ans);
}
return 0;
}

H - Palindrome Number

构造。主要思想是能放$9$就一直放$9$,不能放$9$就放剩余的那个数,注意判断一下不存在的情况。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e6 + 10;
int T;
int n, s;
int ans[maxn]; int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d", &n, &s);
if(n % 2 == 0) {
if(s % 2 || s > n * 9) printf("-1\n");
else {
s = s / 2;
for(int i = 0; i < n / 2; i ++) {
if(s >= 9) ans[i] = 9, s -= 9;
else ans[i] = s, s = 0;
}
for(int i = 0; i < n / 2; i ++) {
printf("%d", ans[i]);
}
for(int i = n / 2 - 1; i >= 0; i --) {
printf("%d", ans[i]);
}
printf("\n");
}
} else {
int p = -1;
for(int i = 0; i <= 9; i ++) {
if((s - i) % 2 != 0) continue;
if((s - i) > 9 * (n - 1)) continue;
p = i;
break;
}
if(p == -1) {
printf("-1\n");
continue;
}
s = (s - p) / 2;
for(int i = 0; i < n / 2; i ++) {
if(s >= 9) ans[i] = 9, s -= 9;
else ans[i] = s, s = 0;
}
if(ans[0] == 0) {
printf("-1\n");
continue;
}
for(int i = 0; i < n / 2; i ++) {
printf("%d", ans[i]);
}
printf("%d", p);
for(int i = n / 2 - 1; i >= 0; i --) {
printf("%d", ans[i]);
}
printf("\n");
}
}
return 0;
}

I - Rock Piles

规律。$dp$打表找一下规律就可以了。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10; int T, n, m;
int dp[1010][1010]; void init() {
for(int i = 0; i <= 1000; i ++) {
dp[0][i] = i % 2;
dp[i][0] = i % 2;
}
for(int i = 1; i <= 1000; i ++) {
for(int j = i; j <= 1000; j ++) {
if(dp[i - 1][j] == 0
|| dp[i][j - 1] == 0
|| dp[i - 1][j - 1] == 0) {
dp[i][j] = 1;
dp[j][i] = 1;
} else {
dp[i][j] = 0;
dp[j][i] = 0;
}
}
}
for(int i = 0; i <= 10; i ++) {
for(int j = i; j <= 10; j ++) {
printf("%d %d %d\n", i, j, dp[i][j]);
}
}
} int main() {
// init();
scanf("%d", &T);
while(T --) {
int ans;
scanf("%d%d", &n, &m);
if(n > m) swap(n, m);
if(n % 2) ans = 1;
else ans = m % 2;
if(ans) printf("hasan\n");
else printf("abdullah\n");
}
return 0;
}

J - Spilt the String

暴力。暴力枚举长度,然后验证一下即可。复杂度大约是$O(n*ln(n))$。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10;
int T;
char s[maxn];
int a[maxn];
int L, R; int work(int x) {
int now;
for(now = L + x; now <= R; now += x) {
if(now > R + 1) return 0;
if(now == R + 1) return 1;
if(a[now - 1] == 1 && a[now] == 0) {
while(a[now] == 0) now ++;
}
else return 0;
}
if(now != R + 1) return 0;
return 1;
} int main() {
scanf("%d", &T);
getchar();
while(T --) {
gets(s);
int len = strlen(s);
for(int i = 0; i < len; i ++) {
if(s[i] == ' ') a[i] = 0;
else a[i] = 1;
}
L = 0, R = len - 1;
while(a[L] == 0) L ++;
while(a[R] == 0) R --;
//printf("%d %d\n", L, R);
if(R < L) {
while(1) {}
printf("YES\n");
continue;
}
int ans = 0;
for(int i = 1; i < len; i ++) {
ans = work(i);
// if(ans) printf("debug %d\n", i);
if(ans) break;
}
if(ans) printf("YES\n");
else printf("NO\n");
}
return 0;
}

K - Two Subarrays

$dp$。枚举$i$位置作为分割,那么答案可能是$[1,i]$中的最大值减去$[i + 1,n]$的最小值,也可以反过来。类似于最大子串和的思路可以搞定。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 10;
int T, n;
long long a[maxn];
long long L[2][maxn][2];
long long R[2][maxn][2];
long long ll[2][maxn];
long long rr[2][maxn]; int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%lld", &a[i]);
}
L[0][1][0] = a[1];
L[1][1][0] = a[1];
L[0][2][0] = a[2];
L[0][2][1] = a[1] - a[2];
L[1][2][0] = a[2];
L[1][2][1] = a[1] - a[2];
for(int i = 3; i <= n; i ++) {
/* min */
L[0][i][0] = min(a[i], L[0][i - 1][1] + a[i]);
L[0][i][1] = L[0][i - 1][0] - a[i];
/* max */
L[1][i][0] = max(a[i], L[1][i - 1][1] + a[i]);
L[1][i][1] = L[1][i - 1][0] - a[i];
} R[0][n][0] = a[n];
R[0][n][1] = -a[n];
R[1][n][0] = a[n];
R[1][n][1] = -a[n];
for(int i = n - 1; i >= 1; i --) {
/* min */
R[0][i][0] = min(a[i], a[i] + R[0][i + 1][1]);
R[0][i][1] = min(-a[i], -a[i] + R[0][i + 1][0]);
/* max */
R[1][i][0] = max(a[i], a[i] + R[1][i + 1][1]);
R[1][i][1] = max(-a[i], -a[i] + R[1][i + 1][0]);
} ll[0][1] = a[1];
ll[1][1] = a[1];
for(int i = 2; i <= n; i ++) {
/* min */
ll[0][i] = min(ll[0][i - 1], min(L[0][i][0], L[0][i][1]));
/* max */
ll[1][i] = max(ll[1][i - 1], max(L[1][i][0], L[1][i][1]));
} rr[0][n] = a[n];
rr[1][n] = a[n];
for(int i = n - 1; i >= 1; i --) {
/* min */
rr[0][i] = min(rr[0][i + 1], R[0][i][0]);
/* max */
rr[1][i] = max(rr[1][i + 1], R[1][i][0]);
} long long ans = 0;
for(int i = 2; i <= n; i ++) {
ans = max(ans, abs(ll[0][i - 1] - rr[1][i]));
ans = max(ans, abs(ll[1][i - 1] - rr[0][i]));
}
printf("%lld\n", ans);
}
return 0;
}

L - The Shortest Path

$spfa$。某点入队超过$n$次就表示存在负环。某点最短路小于图中所有负边权之和,也说明存在负环。

#include <bits/stdc++.h>
using namespace std; const long long INF = 1LL * 6000 * 1e6;
const int maxn = 1e5 + 10;
int T, n, m;
int h[maxn], v[maxn], nx[maxn];
long long w[maxn];
int sz;
long long dis[maxn];
int f[maxn], cnt[maxn];
long long g[2100][2100];
long long sum;
long long ans; void add(int a, int b, long long c) {
v[sz] = b;
w[sz] = c;
nx[sz] = h[a];
h[a] = sz ++;
} void spfa() {
int fail = 0;
for(int i = 0; i <= n; i ++) {
dis[i] = INF;
f[i] = 0;
cnt[i] = 0;
}
queue<int> q;
dis[0] = 0;
f[0] = 1;
q.push(0);
while(!q.empty()) {
int first = q.front();
q.pop();
f[first] = 0;
cnt[first] ++;
if(cnt[first] == n + 1) {
fail = 1;
break;
}
if(dis[first] < sum) {
fail = 1;
break;
}
for(int i = h[first]; i != -1; i = nx[i]) {
if(dis[first] + w[i] < dis[v[i]]) {
dis[v[i]] = dis[first] + w[i];
if(f[v[i]] == 0) {
f[v[i]] = 1;
q.push(v[i]);
}
}
}
}
if(fail) {
printf("-inf\n");
} else {
long long mn = INF;
for(int i = 1; i <= n; i ++) {
mn = min(mn, dis[i]);
}
if(mn == 0) printf("%lld\n", ans);
else printf("%lld\n", min(ans, mn));
}
} int main() {
scanf("%d", &T);
while(T --) {
scanf("%d%d", &n, &m);
for(int i = 0; i <= n; i ++) {
h[i] = -1;
}
sz = 0;
for(int i = 0; i <= n; i ++) {
for(int j = 0; j <= n; j ++) {
g[i][j] = INF;
}
}
for(int i = 1; i <= m; i ++) {
int a, b;
long long c;
scanf("%d%d%lld", &a, &b, &c);
g[a][b] = min(c, g[a][b]);
}
sum = 0;
ans = INF;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
if(g[i][j] == INF) continue;
add(i, j, g[i][j]);
ans = min(ans, g[i][j]);
if(g[i][j] < 0) sum += g[i][j];
}
}
for(int i = 1; i <= n; i ++) {
add(0, i, 0);
}
spfa();
}
return 0;
}

M - Restore Points

暴力。将$d$数组排序,那么最右边的那个点的坐标肯定是$d$数组最后一个值,然后枚举$d$数组倒数第二个值是放在靠左还是靠右,一直枚举下去即可。

#include <bits/stdc++.h>
using namespace std; const int maxn = 1e6 + 10;
int T, n, m;
int d[maxn];
int ans[maxn];
int p[maxn];
int suc; void dfs(int x, int y) {
if(x == n) {
int ok = 1;
for(int i = 0; i < m; i ++) {
if(p[d[i]]) ok = 0;
}
if(ok) suc = 1;
return;
}
if(p[d[y]] == 0) {
dfs(x, y - 1);
return;
}
for(int t = 0; t < 2; t ++) {
int fail = 0;
if(t == 0) ans[x] = d[y];
else ans[x] = ans[1] - d[y];
for(int i = 0; i < x; i ++) {
if(p[abs(ans[x] - ans[i])] == 0) {
fail = 1;
}
}
if(fail) continue;
for(int i = 0; i < x; i ++) {
p[abs(ans[x] - ans[i])] --;
}
dfs(x + 1, y - 1);
if(suc) return;
for(int i = 0; i < x; i ++) {
p[abs(ans[x] - ans[i])] ++;
}
}
} int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
m = n * (n - 1) / 2;
for(int i = 0; i < m; i ++) {
scanf("%d", &d[i]);
p[d[i]] ++;
}
sort(d, d + m);
ans[0] = 0;
ans[1] = d[m - 1];
p[ans[1]] --;
suc = 0;
dfs(2, m - 2);
sort(ans, ans + n);
for(int i = 0; i < n; i ++) {
printf("%d", ans[i]);
if(i < n - 1) printf(" ");
else printf("\n");
}
for(int i = 0; i < m; i ++) {
if(p[d[i]]) {
while(1) {}
}
}
}
return 0;
}