首页 > 其他分享 >CF568C New Language 题解

CF568C New Language 题解

时间:2024-07-25 16:51:18浏览次数:14  
标签:CF568C return int 题解 else char ans print New

Description

  • 将 \(\texttt{a} \sim \texttt{a} + l - 1\) 这 \(l\) 个字符分成 \(\texttt{V,C}\) 两个集合。
  • 你需要构造一个长度为 \(n\)满足 \(m\) 个限制不小于另一个长度为 \(n\) 的字符串 \(s\)最小字符串。
  • 每一个限制为若字符串的第 \(p_1\) 个位置上的字符 \(\in t_1\),则第 \(p_2\) 个位置上的字符 \(\in t_2\)
  • \(l \le 26\),\(n \le 200\),\(m \le 4n(n-1)\)。

Solution

要满足答案不小于 \(s\) 就直接枚举第一个与 \(s\) 不同的位填的字符,然后就是判断能否让一个后缀随便填,使得其满足限制,如果能满足就直接输出最小字符串。

注意到限制类似于 2-SAT,所以先建图。

然后从前往后枚举每一位,如果这一位已经确定就填确定的,否则要选尽量小的填,所以用 tarjan 求 2-SAT 方案显然做不了,但是用 dfs 求 2-SAT 就可以贪心地判断小的是否可行,如果可行就填小的,否则判断大的是否可行,如果还不可行就不存在方案。

具体实现见代码。

时间复杂度:\(O(n^2m)\),但是显然跑不满。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 405;

int l, n, m;
int mi[26][2], op[26], vis[kMaxN];
std::vector<int> G[kMaxN];
std::string s, ans;

bool dfs(int u) {
  if (vis[(u <= n) ? (u + n) : (u - n)]) return 0;
  else if (vis[u]) return 1;
  vis[u] = 1;
  for (auto v : G[u])
    if (!vis[v] && !dfs(v))
      return vis[u] = 0;
  return 1;
}

bool check(int k) { // 前 k 位确定,后面的随便填是否可行
  std::fill_n(vis + 1, 2 * n, 0);
  ans = s;
  for (int i = 1; i <= k; ++i)
    if (!dfs(i + op[s[i] - 'a'] * n))
      return 0;
  for (int i = k + 1; i <= n; ++i) {
    int x = mi[0][0], y = mi[0][1];
    if (!~x) {
      if (!dfs(i + op[y] * n)) return 0;
      ans[i] = char('a' + y);
    } else if (!~y) {
      if (!dfs(i + op[x] * n)) return 0;
      ans[i] = char('a' + x);
    } else if (x < y) {
      if (!dfs(i + op[x] * n)) {
        if (!dfs(i + op[y] * n)) return 0;
        else ans[i] = char('a' + y);
      } else {
        ans[i] = char('a' + x);
      }
    } else {
      if (!dfs(i + op[y] * n)) {
        if (!dfs(i + op[x] * n)) return 0;
        else ans[i] = char('a' + x);
      } else {
        ans[i] = char('a' + y);
      }
    }
  }
  return 1;
}

void print(std::string s) {
  for (int i = 1; i <= n; ++i) std::cout << s[i];
  std::cout << '\n';
}

void dickdreamer() {
  std::string str;
  std::cin >> str >> n >> m;
  l = str.size();
  for (int i = 0; i < l; ++i) op[i] = (str[i] == 'C');
  memset(mi, -1, sizeof(mi));
  for (int i = 0; i < l; ++i) {
    for (int j = l - 1; j >= i; --j) mi[i][op[j]] = j;
  }
  for (int i = 1; i <= m; ++i) {
    int x, y;
    std::string sx, sy;
    std::cin >> x >> sx >> y >> sy;
    int ox = (sx[0] == 'C'), oy = (sy[0] == 'C');
    G[x + ox * n].emplace_back(y + oy * n);
    G[y + (oy ^ 1) * n].emplace_back(x + (ox ^ 1) * n);
  }
  std::cin >> s; s = " " + s;
  if (check(n)) return print(s);
  for (int i = n; i; --i) {
    int now = s[i] - 'a', x = mi[now + 1][0], y = mi[now + 1][1];
    if (!~x && !~y) {
      continue;
    } else if (!~x) {
      s[i] = char(y + 'a');
      if (!check(i)) continue;
      else return print(ans);
    } else if (!~y) {
      s[i] = char(x + 'a');
      if (!check(i)) continue;
      else return print(ans);
    } else {
      if (x < y) {
        s[i] = char(x + 'a');
        if (!check(i)) {
          s[i] = char(y + 'a');
          if (!check(i)) continue;
          else return print(ans);
        } else {
          return print(ans);
        }
      } else {
        s[i] = char(y + 'a');
        if (!check(i)) {
          s[i] = char(x + 'a');
          if (!check(i)) continue;
          else return print(ans);
        } else {
          return print(ans);
        }
      }
    }
    s[i] = char(now + 'a');
  }
  std::cout << "-1\n";
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:CF568C,return,int,题解,else,char,ans,print,New
From: https://www.cnblogs.com/Scarab/p/18323613

相关文章

  • CF22E 题解
    题面注意到题目给的图为基环树森林。因为一个(\(n>1\))的强连通图每个点都要有出度和入度,所以:对于每个基环树,叶子结点是没有入度的,所以一定要有一条从环上出发的路径经过这个点。对于基环树的环,注意到它缩点后没有出度,所以一定要有一条出边。注意到叶子结点的需求和根节点相反,......
  • 题解:AT_arc116_e [ARC116E] Spread of Information
    题目链接思路看到最大值最小首先可以想到二分,发现答案具有单调性,那么思考如何在\(O(n)\)的时间内判断是否合法。考虑贪心,在最远没覆盖的点的距离和要判断的\(mid\)刚好相等的时侯再选择一定不劣,因为这样覆盖的点最多,那么从叶子节点向上回溯,处理它的所有儿子,判断是否选择该......
  • LG3107 [USACO14OPEN] Odometer S 题解 (数位DP+容斥)
    题意定义一个数是神奇的当且仅当这个数中有一个数位出现了一半及以上,比如112,2233。求\([l,r]\)中有多少个好的数字,\(100\lel,r\le10^{18}\)。题解考虑数位DP,先把答案转为\(Ans(r)-Ans(l-1)\),我们钦定一个数\(k\)让他必须出现多于一半,然后我们想求\([1,x]\)中有多少......
  • CF1015F 题解
    题面考虑这样的匹配问题,可以想如何确定第一次匹配,这样可以不重不漏地计数。考虑dp的时候同时维护有几个括号没有匹配,匹配到\(s\)的第几位,所以令\(f(i,j,k)\)表示dp到(要计数的序列的)第\(i\)个字符,有\(j\)个左括号没有匹配,匹配到\(s\)的第\(k\)位。转移很容易,考......
  • 程序设计:C++入门教程(速成) + 15道经典例题(附带例题解析)
    本文章以实用为主,若实在是不明白文字所表达的内容,无脑复制代码,自己动手运行一下,实验一下即可理解文章内容,放心,代码是全的,全选复制粘贴即可不废话,直接开整数据类型常用数据类型int:整数类型,用于表示整数值。例如:1,2,-3,0等。float:单精度浮点数类型,用于表示带有小数点的数......
  • AT_arc116_e [ARC116E] Spread of Information 题解
    题目传送门前置知识二分答案|树形DP解法答案显然具有单调性,考虑二分答案。设当前二分出的答案为\(mid\),则等价于覆盖距离为\(mid\)的情况下进行选点。做法同luoguP3942将军令,考虑进行贪心,对于深度最深的叶节点将选择的点放在边界时,即取\(mid\)级祖先时,覆盖的范......
  • 题解—[ABC265E] Warp
    题面简单计数DP。由于数据范围很大,所以状态不能常规设置为\(dp_{i,j}\)。注意到\(n\)只有\(300\),考虑设\(dp_{i,j,k}\)表示三种行走方法分别使用\(i\),\(j\),\(k\)次的路径数量。状态转移很简单,先计算出来当前所在位置,如果是障碍就跳过,否则$dp_{i,j,k}=dp_{i-1,j,k}+dp......
  • P2671 [NOIP2015 普及组] 求和 题解
    题目:P2671NOIP2015普及组求和题意给定一个带有颜色和数字的序列,我们要寻找三元组\((x,y,z)\)满足以下条件:\(y\)为\(x\)和\(z\)的中点且都为整数。\(color[x]=color[z]\)。我们命这样一个三元组对答案的贡献为\((x+z)*(num[x]+num[z])\)。整个序列的总价值为每个......
  • Temperature 题解
    前言题目链接:洛谷;SPOJ;Hydro&bzoj。题意简述有一个长度为\(n\)的序列,每个位置值的范围为\([L_i,R_i]\)内,求原序列可能的最长不降子串长度。题目分析尝试找一些性质。发现,连续一段合法的区间,都能分成若干真正参与最长不降子串,以及紧跟着的若干包含\(L_i\)的位置。下图......
  • [ABC318E]Sandwiches 题解
    题意给定一个序列\(A\),要求找有多少个三元组\((i,j,k)\)满足以下条件:\(1\lei<j<k\leN\)\(A_i=A_k\)\(A_i\neA_j\)思路相当于是找每两个相同的元素中有多少个不同的数字。例如:12131答案显然是4,即是\((1,2,3)(1,2,5)(1,4,5)(3,4,5)\)。用\(q[A[i]]\)......