解法

这题比赛时过的人很多,我却没思路,糊里糊涂写了个强联通分量,得了 80 分。

这题思路是这样的。

一个替换操作可以看做一个有向边,所以题目实际上给出了一个有向图 $G$,一个节点代表一个字母。

注意题目要求每个操作都必须执行一次。

关于自环

首先注意到自环是没有意义的,因此处理输入时把自环忽略掉。

这里需要特别说明自环的问题,题目描述中并没有说明 $X_i \ne Y_i$。不过似乎可以合理地假设输入中不存在 $X_i = Y_i$ 的操作。

有些 AC 的代码并没有判断自环,比如冰心水蜜桃的提交。当输入中有自环时,这个代码是有 BUG 的。

关于重边

实际上重边也是没有意义的,但是我们不必特别处理它。


用 $\mathsf{h}$ 表示 $G$ 中对应于字符 ‘h’ 的节点。

设字符 $x$ 在串 $S$ 中出现过且 $x$ 不是 ‘h’ 将 $x$ 出现的次数记做 $c_x$ 。则这 $c_x$ 个 $x$ 能转变为 ‘h’ 的充要条件是「图 $G$ 中存在一条从 $x$ 到 ‘h’ 的简单路径」。

证明:不失一般性,设 $x\to x_1 \to x_2 \to \mathsf{h}$ 是一条从 $x$ 到 ‘h’ 的简单路径,则我们可以按下述方法将 $x$ 变为 ‘h’。

首先将 $x$ 变为 $x_1$,这个操作用掉了 $(x\to x_1)$ 这条边;再将剩下的从 $x$ 发出的边全部用掉,这些边将不换改变当前的字符串,然后把所有从 $x$ 发出的边从图 $G$ 中删除。以此类推。

不难注意到若 ‘h’ 有出边,则上述论证是有问题的。

‘h’ 的出度为零的情形是平凡的。考虑 ‘h’ 的出度不为零的情形。此时若图 $G$ 中不存在「从 ‘h’ 到 ‘h’ 的回路」,则若初始字符串 $S$ 中有 ‘h’,则这些 ‘h’ 终将变成别的字符。因此在这种情况下我们可以将 ‘h’ 的所有出边先执行一遍,并把这些边从图 $G$ 中删除。

这样就完成了上述论证。

不过至此我们只是针对一个字符 $x$ 进行论证。实际上对多个字符,结论是一样的,证明留给读者。


现在来考虑图 $G$ 中存在从 ‘h’ 到 ‘h’ 的回路的情形。注意这样的回路一定不是自环。任取一个从 ‘h’ 到 ‘h’ 的回路 $C$,我们可以先把 'h' 变成回路 $C$ 上 'h' 的后继,得到一个新字符串 $S'$,并把图 $G$ 中其他的 ‘h’ 的出边删除。这样就把问题规约为上一段所描述的情形。

实现

我们需要判断的是,对于字符 $x\in S$ 且 $x\ne\mathsf{h}$,图 $G$ 中是否有一条从 $x$ 到 $\mathsf{h}$ 的简单路径,这可以通过 DFS 完成。另外当 $\mathsf{h}$ 的出度不为零时我们需要判断图 $G$ 中是否存在一条从 $\mathsf{h}$ 到 $\mathsf{h}$ 的回路。先进行DFS,确保字符串 $S$ 中所有字符都被访问过。遍历每一条以 $\mathsf{h}$ 为起点的边 $(\mathsf{h} \to x)$,判断图 $G$ 中是否存在从 $x$ 到 $\mathsf{h}$ 的简单路径。

也可以把边反向以后建图,这样只要对 $\mathsf{h}$ 调用一次 DFS 就可以了。

Implementation

#include <bits/stdc++.h>
using namespace std; int main() {
//freopen("main.in", "r", stdin);
int n; cin >> n;
string s; cin >> s;
vector<int> cnt(26);
for(auto ch: s) cnt[ch-'a']++;
vector<vector<int>> g(26);
vector<bool> to_h(26);
bool flag = false;
while (n--) {
char x, y; cin >> x >> y;
if (x != y) {
g[y-'a'].push_back(x-'a');
if(x == 'h') {
to_h[y-'a'] = true;
flag = true;
}
}
} function<void(int)> dfs;
vector<bool> vis(26);
dfs = [&](int u) {
vis[u] = true;
for(auto v: g[u]) {
if(!vis[v]) dfs(v);
}
};
int h = 'h' - 'a';
dfs(h); int ans = 0;
for (int i = 0; i < 26; i++)
if(vis[i]) ans += cnt[i];
if (flag) {
for (int i = 0; i < 26; i++)
if (to_h[i] && vis[i]) {
flag = false;
break;
}
if (flag) ans -= cnt[h];
}
cout << ans << endl;
return 0;
}

最新文章

  1. [LeetCode] Kth Smallest Element in a BST 二叉搜索树中的第K小的元素
  2. 递推 hdu 2064
  3. 为easyui datagrid 添加上下方向键移动
  4. 250W电源带i7+GTX1080?
  5. 边工作边刷题:70天一遍leetcode: day 70
  6. ASP.Net IE10 _doPostBack 未定义错误【转】
  7. linux shell less 命令---转
  8. 16.缓存(Cache)
  9. (转) dedecms中自定义数据模型
  10. MVC4 + EF + System.Threading.Thread 出现的问题记录
  11. 【SDUT 3038】迷之博弈
  12. js复制button在ie下的解决方式
  13. 进阶-案例九: WD中实现export 到Excel,Doc,Txt.
  14. jQuery第九章
  15. sql参数化查询避免注入漏洞的原因探析
  16. 【ASP.NET Core】根据 Content-Type 头部来筛选 Action
  17. Web漏洞扫描工具(批量脱壳、反序列化、CMS)
  18. windows安装gitblit服务端
  19. Zookeeper常用操作命令 ls,ls2,get和stat
  20. oneinstack一键部署linux生产环境那点事(ubuntu)

热门文章

  1. (排班表一)使用SQL语句使数据从坚向排列转化成横向排列
  2. poj_1730_Perfect Pth Powers
  3. MySQL 5.7传统复制到GTID在线切换(一主一从)
  4. Linux Centos 通过虚拟用户访问FTP的配置
  5. LeetCode207 课程表
  6. 总结 Date 2017.09.23
  7. java实时监听日志写入kafka(多目录)
  8. Storm: 集群安装和配置
  9. BZOJ 5004: 开锁魔法II
  10. 分别用反射、编程接口的方式创建DataFrame