「考前日志」11.18

总结

有点活过来了

  • 并查集可以用来维护连通块,还可以用来维护自身的特殊信息。
  • 做题要大胆猜结论。
  • 数据随机的题目考虑乱搞。
  • 对于一类 (a_i-a_jle i - j) 的式子,可以移项搞一下转化为 (a_i-ile{a_j-j}) 从而消除某些限制。
  • 负无穷和 (0) 不是一种东西。

今日已完成

  • 某场模拟赛 网格图

    /*
    知识点:并查集 
    学到了什么:
    并查集可以用来维护连通块,还可以用来维护自身的特殊信息。
    */ 
    const int dx[] = {0, 0, -1, 1};
    const int dy[] = {1, -1, 0, 0};
    char s[A][A];
    int n, m, fa[A * A], ans;
    
    inline int id(int x, int y) {
      return x * (m + 1) + y;
    }
    
    int find(int x) {
      return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    
    namespace Right {
      //表示每个点右边的第一个0 
      //用并查集可以快速维护 
      int ff[A * A];
      int Find(int x) {
        return x == ff[x] ? x : ff[x] = Find(ff[x]);
      } 
      void Union(int x, int y) {
        int fx = Find(x), fy = Find(y);
        ff[fx] = fy;
      } 
    }
    
    int main() {
      n = read(), m = read();
      for (int i = 1; i <= n; i++) scanf("%s", s[i] + 1);
      int all = (n + 1) * (m + 1);
      for (int i = 1; i <= all; i++) fa[i] = Right::ff[i] = i;
      //首先计算出连通块的个数 
      //再之后更新 
      for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
          if (s[i][j] != '1') continue; 
          //先标记为一个新的连通块 
          ans++;
          for (int k = 0; k < 4; k++) {
            int bx = i + dx[k], by = j + dy[k];
            if (s[bx][by] != '1') continue;
            int fz1 = find(id(i, j)), fz2 = find(id(bx, by));
            if (fz1 != fz2) {
              //两者相邻但不为同一连通块
              //要标记成同一连通块,所以ans-- 
              ans--;
              if (rand() & 1) swap(fz1, fz2);
              fa[fz1] = fz2;
            }
          }
          Right::Union(id(i, j), id(i, j + 1));
        }
      }
      cout << ans << '
    ';
      int Q = read();
      while (Q--) {
        int X1 = read(), Y1 = read(), X2 = read(), Y2 = read();
        for (int x = X1; x <= X2; x++) {
          //找x,y1右边的第一个0,看是否在y2左边
          //如果在左边就像上面一样更新,否则直接退出。 
          int pos = Right::Find(id(x, Y1));
          int goal = id(x, Y2);
          while (pos <= goal) {
            int y = pos % (m + 1);
            if (!y) y = m + 1;
            ans++;
            for (int k = 0; k < 4; k++) {
              int bx = x + dx[k], by = y + dy[k];
              if (s[bx][by] != '1') continue;
              int fz1 = find(pos), fz2 = find(id(bx, by));
              if (fz1 != fz2) {
                ans--;
                if (rand() & 1) swap(fz1, fz2);
                fa[fz1] = fz2;
              }
            }
            s[x][y] = '1';
            Right::Union(pos, id(x, y + 1));
            pos = Right::Find(id(x, y));
          }
        }
        cout << ans << '
    ';
      }
    }
    
  • 某场模拟赛 && 洛谷 P1439 最长公共子序列

    这考试竟然考板子,真是爱了爱了。

    int n, a[A], b[A], cx[A], f[A], len = 0;
    
    int main() {
      n = read();
      for (int i = 1; i <= n; i++) a[i] = read(), cx[a[i]] = i;
      for (int i = 1; i <= n; i++) b[i] = read();
      f[++len] = cx[b[1]];
      for (int i = 2; i <= n; i++) {
        if (cx[b[i]] >= f[len]) f[++len] = cx[b[i]];
        else {
          int k = lower_bound(f + 1, f + 1 + len, cx[b[i]]) - f;
          f[k] = cx[b[i]];
        }
      }
      cout << len << '
    ';
      return 0;
    }
    
  • A. 【18提高7】模仿游戏

    数据随机,爆搜+剪枝可过

    void dfs(int cnt) {
      if (cnt == m) {
        for (int i = 0; i < m; i++) cout << a[i] << " ";
        puts("");
        for (int i = 0; i < m; i++) cout << b[i] << " ";
        puts("");
        exit(0);
      }
      for (int i = 0; i < m; i++) {
        if (vis[i]) continue;
        a[cnt] = i;
        tot = 0;
        int flag = 0, siz = ys[cnt].size(); 
        for (int j = 0; j < siz; j++) {
          int xj = ys[cnt][j], goal = (a[cnt] + (xj - 1) / m) % m;
          if (b[goal] != y[xj] && b[goal] != -1) { flag = 1; break; }
          if (b[goal] == -1) c[++tot] = goal, b[goal] = y[xj];
        }
        if (flag) {
          for (int j = 1; j <= tot; j++) b[c[j]] = -1;
          continue;
        }
        vis[i] = 1;
        dfs(cnt + 1);
        vis[i] = 0;
        for (int j = 1; j <= tot; j++) b[c[j]] = -1;
      }
    }
    
    int main() {
      n = read(), m = read();
      for (int i = 1; i <= n; i++) x[i] = read();
      for (int i = 1; i <= n; i++) y[i] = read();
      memset(b, -1, sizeof(b));
      for (int i = 1; i <= n; i++) {
        int goal = (x[i] + i - 1) % m;
        ys[goal].push_back(i);
      }
      dfs(0);
      return 0;
    }
    /*
    0 1 6 10 3 4 5 11 9 8 13 2 7 12 
    7 3 0 4 5 11 1 13 10 2 9 6 12 8 
    */
    
  • 洛谷 P4290 [HAOI2008]玩具取名

    区间DP基础题

    char s[A];
    int ok[5][5][5], f[A][A][5], le[A];
    
    int calc(char s) {
      if (s == 'W') return 1;
      else if (s == 'I') return 2;
      else if (s == 'N') return 3;
      else if (s == 'G') return 4;
    }
    
    int main() {
      for (int i = 1; i <= 4; i++) le[i] = read();
      for (int i = 1; i <= 4; i++)
        for (int j = 1; j <= le[i]; j++) {
          scanf("%s", s);
          ok[i][calc(s[0])][calc(s[1])] = 1;
        }
      scanf("%s", s + 1);
      int n = strlen(s + 1);
      for (int i = 1; i <= n; i++) f[i][i][calc(s[i])] = 1;
      for (int len = 1; len <= n; len++) {
        for (int l = 1; l <= n - len + 1; l++) {
          int r = (l + len);
          for (int mi = l; mi < r; mi++) 
            for (int zt1 = 1; zt1 <= 4; zt1++) if (f[l][mi][zt1]) 
              for (int zt2 = 1; zt2 <= 4; zt2++) if (f[mi + 1][r][zt2]) 
                for (int zt3 = 1; zt3 <= 4; zt3++) if (ok[zt3][zt1][zt2]) f[l][r][zt3] = 1;
        }
      }
      int flag = 0;
      if (f[1][n][1]) flag = 1, cout << "W";
      if (f[1][n][2]) flag = 1, cout << "I";
      if (f[1][n][3]) flag = 1, cout << "N";
      if (f[1][n][4]) flag = 1, cout << "G";
      if (!flag) puts("The name is wrong!");
      return 0;
    }
    
  • CF1433F Zero Remainder Sum

    非常好玩的状态设计

    int n, m, k, a[A][A], f[A][A][A], g[A][A];
    //设f_{j,cnt,r} 表示当前行选了 cnt 个数的和 
    //且 cnt 个数的和 mod k = r
    
    int main() {
      n = read(), m = read(), k = read();
      for (int i = 1; i <= n; i++) 
        for (int j = 1; j <= m; j++) a[i][j] = read();
      memset(g, 0xcf, sizeof(g));
      g[0][0] = 0;
      for (int i = 1; i <= n; i++) {
        memset(f, 0xcf, sizeof(f));
        //debug:把初始化放在了j里 
        for (int j = 0; j <= m; j++) f[j][0][0] = 0;
        for (int j = 1; j <= m; j++) {
          for (int cnt = 1; cnt <= m / 2; cnt++)
            for (int r = 0; r < k; r++)  
              f[j][cnt][r] = max(f[j - 1][cnt][r], f[j - 1][cnt - 1][((r - a[i][j]) % k + k) % k] + a[i][j]);
        }
        for (int j = 0; j < k; j++) {
          for (int cnt = 0; cnt <= m / 2; cnt++)
            for (int r = 0; r < k; r++)
              g[i][j] = max(g[i][j], g[i - 1][((j - f[m][cnt][r]) % k + k) % k] + f[m][cnt][r]);
        }
      }
      cout << max(0, g[n][0]) << '
    ';
      return 0;
    }
    
原文地址:https://www.cnblogs.com/loceaner/p/14003233.html