2018年全国多校算法寒假训练营练习比赛(第五场)题解

Posted Fighting Heart

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018年全国多校算法寒假训练营练习比赛(第五场)题解相关的知识,希望对你有一定的参考价值。

 

题目链接

 

A - 逆序数

经典问题,有很多方法,例如树状数组,线段树,归并排序等。代码不贴了。

 

B - Big Water Problem

单点修改求区间和,树状数组或者线段树都可以。

#include <bits/stdc++.h>
using namespace std;
 
const int maxn = 1e5 + 10;
long long c[maxn];
 
int lowbit(int x) {
  return x & (-x);
}
 
long long sum(int p) {
  long long res = 0;
  while(p) {
    res += c[p];
    p -= lowbit(p);
  }
  return res;
}
 
void update(int x, long long y) {
  while(x < maxn) {
    c[x] += y;
    x += lowbit(x);
  }
}
 
int main() {
   
  int n, m;
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= n; i ++) {
    long long x;
    scanf("%lld", &x);
    update(i, x);
  }
  while(m --) {
    int op;
    scanf("%d", &op);
    if(op == 1) {
      int x;
      long long y;
      scanf("%d%lld", &x, &y);
      update(x, y);
    } else {
      int L, R;
      scanf("%d%d", &L, &R);
      printf("%lld\n", sum(R) - sum(L - 1));
    }
  }
  return 0;
}

 

C - 字符串的问题

比赛中暴力从大到小枚举前缀长度,KMP验证一下就 AC 了?还要思考一下复杂度究竟对不对。

#include <iostream>
#include <cstring>
using namespace std;
 
const int N = 1000010;
int nx[N];
char S[N], T[N];
int slen, tlen;
 
void getNext()
{
  int j, k;
  j = 0; k = -1; nx[0] = -1;
  while(j < tlen)
    if(k == -1 || T[j] == T[k])
      nx[++j] = ++k;
    else
      k = nx[k];
   
}
 
/*
 返回模式串在主串S中出现的次数
 */
int KMP_Count()
{
  int ans = 0;
  int i, j = 0;
   
  if(slen == 1 && tlen == 1)
  {
    if(S[0] == T[0])
      return 1;
    else
      return 0;
  }
  getNext();
  for(i = 0; i < slen; i++)
  {
    while(j > 0 && S[i] != T[j])
      j = nx[j];
    if(S[i] == T[j])
      j++;
    if(j == tlen)
    {
      ans++;
      j = nx[j];
    }
  }
  return ans;
}
 
/*
int main()
{
   
  int TT;
  int i, cc;
  cin>>TT;
  while(TT--)
  {
    cin>>S>>T;
    slen = strlen(S);
    tlen = strlen(T);
    cout<<"模式串T在主串S中首次出现的位置是: "<<KMP_Index()<<endl;
    cout<<"模式串T在主串S中出现的次数为: "<<KMP_Count()<<endl;
  }
  return 0;
}
 */
 
int check(int x) {
  int p1 = 0;
  int p2 = slen - x;
  while(p1 <= x - 1) {
    if(S[p1] != S[p2]) return 0;
    p1 ++;
    p2 ++;
  }
   
  for(int i = 0; i < x; i ++) {
    T[i] = S[i];
    T[i + 1] = 0;
  }
   
  tlen = x;
  if(KMP_Count() >= 3) return 1;
  return 0;
}
 
int main() {
  scanf("%s", S);
  slen = strlen(S);
  int ans = 0;
  int L = 1, R = slen;
  for(int i = slen - 1; i >= 1; i --) {
    if(check(i)) {
      ans = i;
      break;
    }
  }
  if(ans == 0) printf("Just a legend\n");
  else {
    for(int i = 0; i < ans; i ++) {
      printf("%c", S[i]);
    }
    printf("\n");
  }
}

 

D - 集合问题

写了一堆暴力模拟规则就搞过去了,看到有并查集AC的,有待学习。

#include <bits/stdc++.h>
using namespace std;
 
const int maxn = 110000;
int n;
int A, B;
int a[maxn];
map<int, int> belong;
map<int, bool> flag;
 
int main() {
  int fail = 0;
  scanf("%d%d%d", &n, &A, &B);
  for(int i = 1; i <= n; i ++) {
    scanf("%d", &a[i]);
    flag[a[i]] = 1;
  }
  sort(a + 1, a + 1 + n);
  if(A == B) {
    for(int i = 1; i <= n; i ++) {
      if(flag[B - a[i]] == 0) {
        fail = 1;
        break;
      }
      belong[a[i]] = 2;
    }
  } else {
    while(1) {
      if(fail) break;
      int ss = 0;
      for(int i = 1; i <= n; i ++) {
        if(belong[a[i]]) continue;
        if(flag[A - a[i]] == 0 && flag[B - a[i]] == 0) {
          fail = 1;
          break;
        } else if(flag[A - a[i]] == 1 && (flag[B - a[i]] == 0 || belong[B - a[i]])) {
          if(belong[A - a[i]] == 2) {
            fail = 1;
            break;
          }
          belong[a[i]] = 1;
          belong[A - a[i]] = 1;
          ss = 1;
        } else if((flag[A - a[i]] == 0 || belong[A - a[i]]) && flag[B - a[i]] == 1) {
          if(belong[B - a[i]] == 1) {
            fail = 1;
            break;
          }
          belong[a[i]] = 2;
          belong[B - a[i]] = 2;
          ss = 1;
        } else {
          if(A - a[i] != a[i] && B - a[i] != a[i]) {
            if(belong[A - a[i]] == 0 && belong[B - a[i]] == 0) {
              continue;
            } else if(belong[A - a[i]]) {
              belong[a[i]] = belong[A - a[i]];
              ss = 1;
            } else {
              belong[a[i]] = belong[B - a[i]];
              ss = 1;
            }
          } else if(A - a[i] == a[i]) {
            if(belong[B - a[i]] == 1) {
              fail = 1;
              break;
            }
            belong[a[i]] = 2;
            belong[B - a[i]] = 2;
            ss = 1;
          } else {
            if(belong[A - a[i]] == 2) {
              fail = 1;
              break;
            }
            belong[a[i]] = 1;
            belong[A - a[i]] = 1;
            ss = 1;
          }
        }
      }
      if(fail) break;
      if(ss == 0) break;
    }
  }
   
  if(fail == 0) {
    for(int i = 1; i <= n; i ++) {
      if(belong[a[i]] == 1 && belong[A - a[i]] != 1) fail = 1;
      if(belong[a[i]] == 2 && belong[B - a[i]] != 2) fail = 1;
    }
  }
   
  if(fail) {
    printf("NO\n");
  } else {
    printf("YES\n");
    for(int i = 1; i <= n; i ++) {
      printf("%d", belong[a[i]] - 1);
      if(i < n) printf(" ");
      else printf("\n");
    }
  }
   
  return 0;
}

 

E - 情人节的电灯泡

单点更新,求子矩阵和,二维树状数组即可。

#include <bits/stdc++.h>
using namespace std;
 
 
const int maxn = 1010;
int n, m;
 
int C[maxn][maxn];
int a[maxn][maxn];
 
int lowbit(int x) {
  return x & (-x);
}
 
void add(int x, int y, int c){
  for(int i = x; i <= n; i += lowbit(i)){
    for(int j = y; j <= n; j += lowbit(j)){
      C[i][j] += c;
    }
  }
}
 
int sum(int x,int y){
  int ret = 0;
  for(int i = x; i > 0; i -= lowbit(i)){
    for(int j = y;j > 0;j -= lowbit(j)){
      ret += C[i][j];
    }
  }
  return ret;
}
 
int main() {
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= n; i ++) {
    for(int j = 1; j <= n; j ++) {
      scanf("%d", &a[i][j]);
      add(i, j, a[i][j]);
    }
  }
  while(m --) {
    int op;
    scanf("%d", &op);
    if(op == 1) {
      int x, y;
      scanf("%d%d", &x, &y);
      if(a[x][y] == 1) {
        add(x, y, -1);
      } else {
        add(x, y, 1);
      }
      a[x][y] = (a[x][y] ^ 1);
    } else {
      int x1, y1, x2, y2;
      scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
      printf("%d\n", sum(x2, y2) - sum(x1 - 1, y2) - sum(x2, y1 - 1) + sum(x1-1, y1-1));
    }
  }
  return 0;
}

 

F - The Biggest Water Problem

模拟一下就可以了。

#include <bits/stdc++.h>
using namespace std;

long long n;

long long work(long long x) {
  long long res = 0;
  while(x) {
    res += x % 10;
    x = x / 10;
  }
  return res;
}

int main() {
  scanf("%d", &n);
  while(1) {
    if(n <= 9) {
      printf("%lld\n", n);
      break;
    }
    n = work(n);
  }
  return 0;
}

 

G - 送分啦-QAQ

该题和HDU 2516一样

 

H - Tree Recovery

区间加,区间求和,线段树就可以了。

#include <bits/stdc++.h>
using namespace std;
 
const int maxn = 1e5 + 10;
 
int n, Q;
long long sum[4 * maxn];
long long f[4 * maxn];
 
void pushDown(int l, int r, int rt) {
  if(f[rt] == 0) return;
  int mid = (l + r) / 2;
  int len_left = (mid - l + 1);
  int len_right = (r - mid);
  f[2 * rt] += f[rt];
  f[2 * rt + 1] += f[rt];
  sum[2 * rt] += f[rt] * len_left;
  sum[2 * rt + 1] += f[rt] * len_right;
  f[rt] = 0;
}
 
void pushUp(int rt) {
  sum[rt] =sum[2 * rt] + sum[2 * rt + 1];
}
 
void build(int l, int r, int rt) {
  if(l == r) {
    scanf("%lld", &sum[rt]);
    return;
  }
  int mid = (l + r) / 2;
  build(l, mid, 2 * rt);
  build(mid + 1, r, 2 * rt + 1);
  pushUp(rt);
}
 
void update(int L, int R, long long val, int l, int r, int rt) {
  if(L <= l && r <= R) {
    sum[rt] += val * (r - l + 1);
    f[rt] += val;
    return;
  }
  pushDown(l, r, rt);
  int mid = (l + r) / 2;
  if(L <= mid) update(L, R, val, l, mid, 2 * rt);
  if(R > mid) update(L, R, val, mid + 1, r, 2 * rt + 1);
  pushUp(rt);
}
 
long long query(int L, int R, int l, int r, int rt) {
  if(L <= l && r <= R) {
    return sum[rt];
  }
  pushDown(l, r, rt);
  int mid = (l + r) / 2;
  long long left = 0;
  long long right = 0;
  if(L <= mid) left = query(L, R, l, mid, 2 * rt);
  if(R > mid) right = query(L, R, mid + 1, r, 2 * rt + 1);
  pushUp(rt);
  return left + right;
}
 
int main() {
  scanf("%d%d", &n, &Q);
  build(1, n, 1);
  while(Q --) {
    char op[10];
    scanf("%s", op);
    if(op[0] == ‘Q‘) {
      int L, R;
      scanf("%d%d", &L, &R);
      printf("%lld\n", query(L, R, 1, n, 1));
    } else {
      int L; int R;
      long long val;
      scanf("%d%d%lld", &L, &R, &val);
      update(L, R, val, 1, n, 1);
    }
  }
  return 0;
}

 

以上是关于2018年全国多校算法寒假训练营练习比赛(第五场)题解的主要内容,如果未能解决你的问题,请参考以下文章

2018年全国多校算法寒假训练营练习比赛(第五场):A题:逆序数

2018年全国多校算法寒假训练营练习比赛(第五场)-C字符串问题(KMP)

2018年全国多校算法寒假训练营练习比赛(第五场)-E情人节的电灯泡(二维树状数组单点更新+区间查询)

牛客网NowCoder 2018年全国多校算法寒假训练营练习比赛(第五场)A.逆序数 B.Big Water Problem(线段树-区间查询求和和单点更新) F.The Biggest Water

2018年全国多校算法寒假训练营练习比赛(第四场)

2018年全国多校算法寒假训练营练习比赛(第二场)