2017 ACM Amman Collegiate Programming Contest 题解

 

题目链接

 

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;
}

 

posted @ 2018-02-02 11:01  Fighting_Heart  阅读(946)  评论(0编辑  收藏  举报