zoukankan      html  css  js  c++  java
  • 牛客小白月赛15

    题目传送门

    官方题解传送门

    -------------------这次的题目出的不错,有9题是我赛后能做出来的。但是数据太智障了,重配好几次还是有问题。

    A .斑羚飞渡

    sol:贪心:如果x[i] + y[i] < m,则第i只斑羚一定到不了对岸,所以要尽量多的使用这种斑羚当跳板;如果x[i] + y[i] >= m,则两只这样的斑羚一定有一只可以借助另一只到达对岸;

    ps:比赛的时候数据出锅了,重配之后还是有锅,还好比赛的时候没花时间在这题上面。

    • 贪心
      #include "bits/stdc++.h"
      using namespace std;
      vector<int> v1, v2;
      int n, m, ans;
      int main() {
          int x, y;
          scanf("%d%d", &n, &m);
          for (int i = 1; i <= n; i++) {
              scanf("%d%d", &x, &y);
              if (x >= m) {
                  ans++;
                  continue;
              }
              if (x + y >= m) v1.push_back(y);
              else v2.push_back(x);
          }
          sort(v1.begin(), v1.end());
          sort(v2.begin(), v2.end());
          for (int i = 0; i < v2.size(); i++) {
              if (v2[i] + v1.back() >= m) {
                  v1.pop_back();
                  ans++;
              }
          }
          printf("%d
      ", ans + v1.size() / 2);
          return 0;
      }
      View Code

    B .诡异的因数

    sol:相当暴力。t * n都能过,优化一下就是t * sqrt(n);

    • t * sqrt(n)的暴力
      #include "bits/stdc++.h"
      using namespace std;
      int main() {
          int t, n;
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              int cnt = 0;
              for (int i = 1; i * i <= n; i++) {
                  if (n % i == 0) {
                      cnt++;
                      if (n / i != i) cnt++;
                  }
              }
              printf("%d
      ", cnt);
          }
          return 0;
      }
      View Code

    sol:觉得还有更优的打表方法,但是没想到。去代码区看了一下效率最高的代码果然是打表。值得学习;

    • 打表预处理
      #include "bits/stdc++.h"
      using namespace std;
      const int MAXN = 1e4 + 5;
      int cnt[MAXN];
      void init() {
          for (int i = 1; i <= 10000; i++) {
              if (i * i <= 10000) cnt[i * i]++;
              for (int j = i + 1; i * j <= 10000; j++) cnt[i * j] += 2;
          }
      }
      int main() {
          init();
          int t, n;
          scanf("%d", &t);
          while (t--) {
              scanf("%d", &n);
              printf("%d
      ", cnt[n]);
          }
          return 0;
      }
      View Code

    C .表单

    sol:用一个set记录已经出现的串,一个cnt记录上一次去重之后出现了几个重复串。

    ps:我被这题坑到了,原先我一直用C语言风格的输入输出。但是scanf不方便输入string类型。所以就采用了cin,后来得知这题的整型用cin输入会wa。不知道是什么数据造成的。而且数据里还有空行之前还没说明,而且op还有非1非2的情况。

    • 事先记录
      #include "bits/stdc++.h"
      using namespace std;
      string s;
      set<string> st;
      int main() {
          int n, q, op, cnt = 0;
          scanf("%d%d", &n, &q);
          while (n--) {
              cin >> s;
              if (st.count(s)) cnt++;
              else st.insert(s);
          }
          while (q--) {
              scanf("%d", &op);
              if (op == 1) {
                  cin >> s;
                  if (st.count(s)) cnt++;
                  else st.insert(s);
              } else {
                  printf("%d
      ", cnt);
                  cnt = 0;
              }
          }
          return 0;
      }
      View Code

    D .分数的运算

    sol:加减通分,乘除直接算。减法是加法逆运算,除法是乘法逆运算。写俩自定义函数就完事。

    • 数学
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      void add(LL a, LL b, LL c, LL d) {
          LL g = __gcd(b, d);
          LL x = a * d / g + c * b / g, y = b * d / g;
          if (y < 0) x = -x, y = -y;
          if (x == 0) {
              puts("0 0");
              return;
          }
          g = __gcd(x, y);
          printf("%lld %lld
      ", x / g, y / g);
      }
      void mul(LL a, LL b, LL c, LL d) {
          LL x = a * c, y = b * d;
          if (x == 0) {
              puts("0 0");
              return;
          }
          LL g = __gcd(x, y);
          printf("%lld %lld
      ", x / g, y / g);
      }
      int main() {
          LL a, b, c, d;
          scanf("%lld%lld%lld%lld", &a, &b, &c, &d);
          add(a, b, c, d);
          add(a, b, -c, d);
          mul(a, b, c, d);
          mul(a, b, d, c);
          return 0;
      }
      View Code

    E .希望

    sol:就是一个01背包,但是每个物品的最小费用不明确。要解决这个问题,可以用线段树;

    • 线段树+01背包
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int MAXN = 1e5 + 5;
      const int INF = 0x3f3f3f3f;
      struct Node {
          int l, r;
          int _min;
      } segTree[MAXN * 4];
      int a[MAXN], b[MAXN], c[MAXN];
      int dp[MAXN];
      void build(int p, int l, int r) {
          segTree[p].l = l;
          segTree[p].r = r;
          segTree[p]._min = INF;
          if (l != r) {
              int mid = l + r >> 1;
              build(p << 1, l, mid);
              build(p << 1 | 1, mid + 1, r);
          }
      }
      void update(int p, int l, int r, int x) {
          if (segTree[p].l >= l && segTree[p].r <= r) {
              segTree[p]._min = min(segTree[p]._min, x);
              return;
          }
          int mid = segTree[p].l + segTree[p].r >> 1;
          if (l <= mid) update(p << 1, l, r, x);
          if (r > mid) update(p << 1 | 1, l, r, x);
      }
      int query(int p, int i) {
          if (segTree[p].l == segTree[p].r) return segTree[p]._min;
          int mid = segTree[p].l + segTree[p].r >> 1;
          if (i <= mid) return min(query(p << 1, i), segTree[p]._min);
          else return min(query(p << 1 | 1, i), segTree[p]._min);
      }
      int main() {
          int n, k, m, l, r, x;
          scanf("%d%d%d", &n, &k, &m);
          for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
          build(1, 1, n);
          for (int i = 1; i <= m; i++) {
              scanf("%d%d%d", &l, &r, &x);
              update(1, l, r, x);
          }
          LL ans = 0, tot = 0;
          for (int i = 1; i <= n; i++) {
              ans += a[i];
              if (a[i] < 0) {
                  b[++tot] = -a[i];
                  c[tot] = query(1, i);
              }
          }
          for (int i = 1; i <= tot; i++) {
              for (int j = k; j >= c[i]; j--) {
                  dp[j] = max(dp[j], dp[j - c[i]] + b[i]);
              }
          }
          printf("%lld
      ", ans + dp[k]);
          return 0;
      }
      View Code

      线段树里的_min有点像懒标记,但是不用向下扩展。

    sol:线段树的做法是从别人那看来的,看完发现其实我可以用并查集来代替线段树的功能,而且更高效;

    • 并查集+01背包
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const int MAXN = 1e5 + 5;
      const int INF = 0x3f3f3f3f;
      struct Node {
          int l, r, x;
      } p[MAXN];
      int a[MAXN], b[MAXN], c[MAXN];
      int pre[MAXN], _min[MAXN], dp[MAXN];
      bool cmp(Node a, Node b) {
          return a.x < b.x;
      }
      int find(int k) {
          if (pre[k] == k) return k;
          return pre[k] = find(pre[k]);
      }
      int main() {
          int n, k, m;
          scanf("%d%d%d", &n, &k, &m);
          for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
          for (int i = 1; i <= m; i++) scanf("%d%d%d", &p[i].l, &p[i].r, &p[i].x);
          sort(p + 1, p + 1 + m, cmp);
          for (int i = 1; i <= n; i++) pre[i] = i, _min[i] = INF;
          for (int i = 1; i <= m; i++) {
              for (int j = find(p[i].r); j >= p[i].l; j = find(j)) {
                  _min[j] = p[i].x;
                  pre[j] = find(j - 1);
              }
          }
          LL ans = 0, tot = 0;
          for (int i = 1; i <= n; i++) {
              ans += a[i];
              if (a[i] < 0) {
                  b[++tot] = -a[i];
                  c[tot] = _min[i];
              }
          }
          for (int i = 1; i <= tot; i++) {
              for (int j = k; j >= c[i]; j--) {
                  dp[j] = max(dp[j], dp[j - c[i]] + b[i]);
              }
          }
          printf("%lld
      ", ans + dp[k]);
          return 0;
      }
      View Code

      和hdu1698一样,这种题目也做了好几道了,每次都用线段树解一遍,并查集解一遍。01背包就是很基础的东西了。

    F .跳跃的旋律

    sol:用离线分块,之前没怎么看过分块,算是了解一下吧。代码很好理解

    ps:这题的数据真垃圾,一开始说是暴力都能AC。然后出题人重配了数据之后号称卡飞暴力,然后就把正解也卡了。我一样的代码交上去一回儿通过一回儿超时的。而且这题现在的数据不开快读怕是过不去了吧。

    • 分块
      #include "bits/stdc++.h"
      using namespace std;
      const int MAXN = 500010;
      const int MOD = 20180718;
      int a[MAXN], dp[MAXN], ans[MAXN];
      struct Node {
          int x, y, id;
      } q[MAXN];
      bool cmp(Node a, Node b) {
          return a.y < b.y;
      }
      inline int read() {
          int n = 0; char c = getchar();
          while (c < '0' || c > '9') c = getchar();
          while (c >= '0' && c <= '9') {
              n = n * 10 + (c ^ '0');
              c = getchar();
          }
          return n;
      }
      int main() {
          int n = read(), m = read(), tot = 0, x, y;
          for (int i = 1; i <= n; i++) a[i] = read();
          for (int i = 1; i <= m; i++) {
              x = read(), y = read();
              if (1LL * y * y >= n) {
                  int tmp = 1, j = x;
                  while (j <= n) {
                      tmp = 1LL * tmp * a[j] % MOD;
                      j += y;
                  }
                  ans[i] = tmp;
              } else {
                  ++tot;
                  q[tot].x = x, q[tot].y = y;
                  q[tot].id = i;
              }
          }
          sort(q + 1, q + 1 + tot, cmp);
          int d = 0;
          for (int i = 1; i <= tot; i++) {
              if (q[i].y == d) {
                  ans[q[i].id] = dp[q[i].x];
                  continue;
              }
              d = q[i].y;
              for (int j = n; j >= 1; j--) {
                  if (j + d > n) dp[j] = a[j];
                  else dp[j] = 1LL * a[j] * dp[j + d] % MOD;
              }
              ans[q[i].id] = dp[q[i].x];
          }
          for (int i = 1; i <= m; i++) printf("%d
      ", ans[i]);
          return 0;
      }
      View Code

    G .FTOS的测试

    sol:既然没一个人做出来那我就跳过了吧,连出题人都懒得敲的代码(好奇没有标程数据是怎么来的);

    H .数据结构

    sol:我是用分块,块内排序二分找相同。这是最好想最好敲的方案了;

    ps:这题还考细心,x的范围到10W。而且l可能比r大,比赛结束都没发现这个问题。吃个教训长记性吧。这题比赛的时候数据也出锅,结果重判后还是没通过orz

    • 分块
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const LL MOD = 20180623;
      const LL MAXN = 10010;
      vector<LL> v[MAXN];
      int main() {
          LL n, m, k;
          scanf("%lld%lld", &n, &m);
          for (LL i = 1; i <= n; i++) {
              scanf("%lld", &k);
              v[k].push_back(i);
          }
          for (LL i = 0; i <= 10000; i++) sort(v[i].begin(), v[i].end());
          LL l1, r1, l2, r2, x;
          while (m--) {
              scanf("%lld%lld%lld%lld%lld", &l1, &r1, &l2, &r2, &x);
              if (x > 10000) {
                  printf("0
      0
      0
      ");
                  continue;
              }
              if (l1 > r1) swap(l1, r1); if (l2 > r2) swap(l2, r2);
              LL a = upper_bound(v[x].begin(), v[x].end(), r1) - lower_bound(v[x].begin(), v[x].end(), l1);
              LL b = upper_bound(v[x].begin(), v[x].end(), r2) - lower_bound(v[x].begin(), v[x].end(), l2);
              printf("%lld
      %lld
      %lld
      ", a % MOD, b % MOD, a * b % MOD);
          }
          return 0;
      }
      View Code

       听说还可以用什么线段树主席树,树状数组套平衡树来做。但是我不会,哪天会了再来敲。

    2019/6/21学完主席树来补题

    sol:主席树,一开始用朴素的主席树做了一遍,但是感觉有效的只有叶子节点,大量的非叶子节点完全浪费了。然后就把树给稍作休整进行优化了。

    • 朴素主席树
      #include "cstdio"
      #include "algorithm"
      using namespace std;
      const int MAXN = 100010;
      const int MOD = 20180623;
      struct Node {
          int lson, rson;
          int cnt;
      } tree[MAXN * 20];
      int tot, root[MAXN];
      int build(int l, int r) {
          int i = ++tot;
      //  tree[i].cnt = 0;
          if (l != r) {
              int mid = l + r >> 1;
              tree[i].lson = build(l, mid);
              tree[i].rson = build(mid + 1, r);
          }
          return i;
      }
      int add(int pre, int l, int r, int index) {
          int i = ++tot;
          tree[i] = tree[pre];
          if (l == r) {
              tree[i].cnt++;
          } else {
              int mid = l + r >> 1;
              if (index <= mid) tree[i].lson = add(tree[pre].lson, l, mid, index);
              else tree[i].rson = add(tree[pre].rson, mid + 1, r, index);
          }
          return i;
      }
      int query(int i, int l, int r, int index) {
          if (l == r) return tree[i].cnt;
          int mid = l + r >> 1;
          if (index <= mid) return query(tree[i].lson, l, mid, index);
          else return query(tree[i].rson, mid + 1, r, index);
      }
      int main() {
          int n, m, k;
          scanf("%d%d", &n, &m);
          root[0] = build(1, 10000);
          for (int i = 1; i <= n; i++) {
              scanf("%d", &k);
              root[i] = add(root[i - 1], 1, 10000, k);
          }
          int l1, r1, l2, r2, x;
          while (m--) {
              scanf("%d%d%d%d%d", &l1, &r1, &l2, &r2, &x);
              if (x > 10000) {
                  printf("0
      0
      0
      ");
                  continue;
              }
              if (l1 > r1) swap(l1, r1);
              if (l2 > r2) swap(l2, r2);
              int a = query(root[r1], 1, 10000, x) - query(root[l1 - 1], 1, 10000, x);
              int b = query(root[r2], 1, 10000, x) - query(root[l2 - 1], 1, 10000, x);
              int c = 1LL * a * b % MOD;
              printf("%d
      %d
      %d
      ", a, b, c);
          }
          return 0;
      }
      View Code
    • 优化版主席树
      #include "bits/stdc++.h"
      using namespace std;
      const int MAXN = 100010;
      const int MOD = 20180623;
      struct Node {
          int lson, rson;
          int cnt;
      } tree[MAXN * 20];
      int tot, root[MAXN];
      // 利用二进制决定一个数字是父节点的左儿子还是右儿子
      // 如 9 = 1001 从根节点出发就是右左左,从低位往高位扫,最高位的1忽略; 
      void build(int i) {
          int now = 0;
          while (i != 1) {
              if (i & 1) {
                  if (tree[now].rson == 0) tree[now].rson = ++tot;
                  now = tree[now].rson;
              } else {
                  if (tree[now].lson == 0) tree[now].lson = ++tot;
                  now = tree[now].lson;
              }
              i >>= 1;
          }
      }
      int add(int pre, int index) {
          int i = ++tot;
          tree[i] = tree[pre];
          if (index == 1) {
              tree[i].cnt++;
          } else {
              if (index & 1) tree[i].rson = add(tree[pre].rson, index >> 1);
              else tree[i].lson = add(tree[pre].lson, index >> 1);
          }
          return i;
      }
      int query(int i, int index) {
          if (index == 1) return tree[i].cnt;
          if (index & 1) return query(tree[i].rson, index >> 1);
          else return query(tree[i].lson, index >> 1);
      }
      int main() {
          int n, m, k;
          scanf("%d%d", &n, &m);
      //  root[0] = 0;
          for (int i = 2; i <= 10000; i++) build(i);
          for (int i = 1; i <= n; i++) {
              scanf("%d", &k);
              root[i] = add(root[i - 1], k);
          }
          int l1, r1, l2, r2, x;
          while (m--) {
              scanf("%d%d%d%d%d", &l1, &r1, &l2, &r2, &x);
              if (x > 10000) {
                  printf("0
      0
      0
      ");
                  continue;
              }
              if (l1 > r1) swap(l1, r1);
              if (l2 > r2) swap(l2, r2);
              int a = query(root[r1], x) - query(root[l1 - 1], x);
              int b = query(root[r2], x) - query(root[l2 - 1], x);
              int c = 1LL * a * b % MOD;
              printf("%d
      %d
      %d
      ", a, b, c);
          }
          return 0;
      }
      View Code

      优化后一棵树的大小为10000,每个节点都有效。但是由于路径问题,还是有一些重复的节点无法删除。

    I .y大的字符串

    sol:Trie+manacher,比赛的时候没开这题有点可惜,Trie是我最早学的数据结构,敲的很熟练但是没在比赛中碰到过。这次还是比赛结束之后看到题解补的。

    ps:数据还是有问题,1M的字符串长度是1024 * 1024。但是字符串远不止这个长度。

    • Trie + manacher
      #include "cstdio"
      #include "cstdlib"
      #include "cstring"
      #include "algorithm"
      using namespace std;
      const int MAXN = 10250 * 1025;
      char s[15], t[MAXN * 2];
      bool head[MAXN];
      int p[MAXN * 2];
      int _max;
      struct Trie {
          Trie* next[26];
          bool tail;
      } *root;
      int max(int a, int b) {return a > b ? a : b;}
      int min(int a, int b) {return a < b ? a : b;}
      Trie* init() {
          Trie* p = (Trie*)malloc(sizeof(Trie));
          memset(p->next, NULL, sizeof(p->next));
          p->tail = false;
          return p;
      }
      void insert(char* s) {
          Trie* p = root;
          for (int i = 0; s[i]; i++) {
              int j = s[i] - 'a';
              if (!p->next[j]) p->next[j] = init();
              p = p->next[j];
          }
          p->tail = true;
      }
      void search(char* s) {
          Trie* p = root;
          for (int i = 0; s[i]; i++) {
              int j = s[i] - 'a';
              if (!p->next[j]) return;
              p = p->next[j];
              if (p->tail) {
                  head[s + i - t + 1] = true;
                  _max = max(_max, s + i - t + 1);
              }
          }
      }
      int manacher(char* s, int* p) {
          int n = strlen(s);
          for (int i = n; i >= 0; i--) {
              s[i + 1 << 1] = s[i];
              s[i << 1 | 1] = '#';
          }
          s[0] = '$';
          n = n + 1 << 1;
          int _max = 1, k = 0;
          for (int i = 2; s[i]; i++) {
              if (i >= k + p[k]) p[i] = 1;
              else p[i] = min(p[2 * k - i], k + p[k] - i);
              while (s[i + p[i]] == s[i - p[i]]) p[i]++;
              if (p[i] + i > p[k] + k) k = i;
              _max = max(_max, p[i]);
          }
          return _max - 1;
      }
      int main() {
          int n, m;
          scanf("%d%d", &n, &m);
          root = init();
          while (n--) {
              scanf("%s", s);
              insert(s);
          }
          while (m--) {
              scanf("%s", t);
              _max = 0;
              memset(head, false, sizeof(head));
              head[0] = true;
              for (int i = 0; t[i]; i++) if (head[i]) search(t + i);
              t[_max] = '';
              printf("%d %d
      ", _max, manacher(t, p));
          }
          return 0;
      }
      View Code

      牛客的库函数里居然没有min和max。

    J .外挂

    sol:推出公式后用线段树维护,推公式需要比较强的数学基础,但是线段树还是可以敲的;

    • 线段树
      #include "bits/stdc++.h"
      using namespace std;
      typedef long long LL;
      const LL MAXN = 1e5 + 5;
      struct Node {
          LL l, r;
          // s1表示区间和,s2表示区间平方和 
          LL s1, s2;
          LL lazy;
      } segTree[MAXN * 4];
      void push_up(LL p, LL x) {
          segTree[p].lazy += x;
          segTree[p].s2 += 2 * segTree[p].s1 * x + x * x * (segTree[p].r - segTree[p].l + 1);
          segTree[p].s1 += x * (segTree[p].r - segTree[p].l + 1);
      }
      void push_down(LL p) {
          if (segTree[p].l == segTree[p].r) return;
          push_up(p << 1, segTree[p].lazy);
          push_up(p << 1 | 1, segTree[p].lazy);
          segTree[p].lazy = 0;
      }
      void build(LL p, LL l, LL r) {
          segTree[p].l = l;
          segTree[p].r = r;
          if (l == r) {
              scanf("%lld", &segTree[p].s1);
              segTree[p].s2 = segTree[p].s1 * segTree[p].s1;
              return;
          }
          LL mid = l + r >> 1;
          build(p << 1, l, mid);
          build(p << 1 | 1, mid + 1, r);
          segTree[p].s1 = segTree[p << 1].s1 + segTree[p << 1 | 1].s1;
          segTree[p].s2 = segTree[p << 1].s2 + segTree[p << 1 | 1].s2;
      }
      void add(LL p, LL l, LL r, LL x) {
          if (segTree[p].l >= l && segTree[p].r <= r) {push_up(p, x); return;}
          push_down(p);
          LL mid = segTree[p].l + segTree[p].r >> 1;
          if (l <= mid) add(p << 1, l, r, x);
          if (r > mid) add(p << 1 | 1, l, r, x);
          segTree[p].s1 = segTree[p << 1].s1 + segTree[p << 1 | 1].s1;
          segTree[p].s2 = segTree[p << 1].s2 + segTree[p << 1 | 1].s2;
      }
      LL getS1(LL p, LL l, LL r) {
          if (segTree[p].l == l && segTree[p].r == r) return segTree[p].s1;
          push_down(p);
          LL mid = segTree[p].l + segTree[p].r >> 1;
          if (r <= mid) return getS1(p << 1, l, r);
          if (l > mid) return getS1(p << 1 | 1, l, r);
          return getS1(p << 1, l, mid) + getS1(p << 1 | 1, mid + 1, r);
      }
      LL getS2(LL p, LL l, LL r) {
          if (segTree[p].l == l && segTree[p].r == r) return segTree[p].s2;
          push_down(p);
          LL mid = segTree[p].l + segTree[p].r >> 1;
          if (r <= mid) return getS2(p << 1, l, r);
          if (l > mid) return getS2(p << 1 | 1, l, r);
          return getS2(p << 1, l, mid) + getS2(p << 1 | 1, mid + 1, r);
      }
      int main() {
          LL n, m;
          scanf("%lld%lld", &n, &m);
          build(1, 1, n);
          LL op, l, r, x;
          while (m--) {
              scanf("%lld", &op);
              if (op == 1) {
                  scanf("%lld%lld%lld", &l, &r, &x);
                  add(1, l, r, x);
              } else {
                  scanf("%lld%lld", &l, &r);
                  LL s1 = getS1(1, l, r);
                  LL s2 = getS2(1, l, r);
                  printf("%lld
      ", s1 * s1 - s2 >> 1);
              }
          }
          return 0;
      } 
      View Code
  • 相关阅读:
    mysql给数据库字段赋值为随机数
    利用lList集合中的subList进行分页
    redis中分页缓存数据
    ios账号第三方登录,判断是否是Ios账号
    通过ip查询ip地址
    MySQL
    排序算法
    139. 单词拆分
    138. 复制带随机指针的链表
    137. 只出现一次的数字 II
  • 原文地址:https://www.cnblogs.com/Angel-Demon/p/11042575.html
Copyright © 2011-2022 走看看