题意:给一棵\(n\)个节点的二叉树,每条边上有一个小写字母或者\(?\),\(q\)次修改操作,每次修改某条边上的字符,问修改后是否存在一种方案,使得给所有\(?\)填上小写字母后,所有叶子到根的路径字符串经重排后是否能全部相同,若能,还需要求出每种字符在这个字符串里的最多出现次数。

题解:考虑怎样处理单次询问。

首先显然每个叶子节点的深度必须是一样的,记为\(dep\)。

初步的想法显然是计算出根到每个叶子节点中每一种字符(\(?\)除外)出现的最大次数,设\(26\)种字符出现的最大次数总和为\(sum\),那么有合法方案当且仅当\(dep \le sum\)。但是这样是假的,因为\(?\)不独立。考虑修改上述算法,其实只要对以每一个节点为根的子树都用上述算法判断一遍是否合法即可。

证明:必要性显然。

充分性只要证明,对于任意一种最终的字符串,只要对于每个字符都满足字符串中这种字符的出现次数\(\ge\)每条根到叶子的路径中这种字符出现的最大次数,那么就是一个合法的字符串。这能很容易地通过按树深度归纳证明。

以上证明过程也告诉了我们怎样计算最大出现次数。

考虑怎样处理修改。显然可以ddp可以发现,对于只有一个儿子的节点,可以把它和儿子缩起来。这样树的深度会变成\(O(\sqrt n)\)级别的。证明:设\(s_i\)为深度为\(i\)的节点数,那么第\(i\)层没有被缩完的话需要满足\(s_i>s_{i-1}\),所以最坏情况下\(s=1,2,3,...,O(\sqrt n)\)。因为保证了最大度数为\(2\),所以更新dp值的时候暴力就可以了。

#include<bits/stdc++.h>
using namespace std;
const int N = 150010;
typedef vector<int> vi;
#define pb push_back int gi() {
int x = 0, o = 1;
char ch = getchar();
while((ch < '0' || ch > '9') && ch != '-') {
ch = getchar();
}
if(ch == '-') {
o = -1, ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = x * 10 + ch - '0', ch = getchar();
}
return x * o;
} int n, q, fa[N], len[N], id[N], cnt[N][26], ban = 0, sum[N], f[N][26];
vi E[N], G[N];
char c[N]; int dfs(int u) {
int son = 0;
for(auto v : E[u]) {
++son;
dfs(v);
if(len[u] && len[u] != len[v] + 1) {
while(q--) {
puts("Fou");
}
exit(0);
}
len[u] = len[v] + 1;
id[u] = id[v];
}
if(son != 1 || !u) {
id[u] = u;
for(auto v : E[u]) {
fa[id[v]] = u, G[u].pb(id[v]);
}
}
return id[u];
} void upd(int u, int c, int w) {
cnt[u][c] += w;
for(int x = fa[u]; ~x; x = fa[x]) {
ban -= sum[x] > len[x];
sum[x] -= f[x][c];
f[x][c] = 0;
for(auto v : G[x]) {
f[x][c] = max(f[x][c], f[v][c] + cnt[v][c]);
}
sum[x] += f[x][c];
ban += sum[x] > len[x];
}
} int main() {
#ifndef ONLINE_JUDGE
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
#endif
cin >> n >> q;
for(int i = 2; i <= n; i++) {
E[gi()].pb(i), c[i] = getchar();
}
E[0].pb(1);
dfs(0);
--len[0];
fa[0] = -1;
for(int i = 2; i <= n; i++) if(c[i] != '?') {
upd(id[i], c[i] - 'a', 1);
}
while(q--) {
int u = gi();
if(c[u] != '?') {
upd(id[u], c[u] - 'a', -1);
}
c[u] = getchar();
if(c[u] != '?') {
upd(id[u], c[u] - 'a', 1);
}
if(ban) {
puts("Fou");
} else {
cout << "Shi ";
int ans = 0;
for(int i = 0; i < 26; i++) {
ans += (i + 1) * (f[0][i] + len[0] - sum[0]);
}
cout << ans << '\n';
}
}
return 0;
}

[CF1168D]Anagram Paths的更多相关文章

  1. RE:ゼロから始める文化課生活

    觉得有必要在NOI之前开一篇学习内容记录. 至于为什么要取这个标题呢?也许并没有什么特殊的借口吧. 5.23 在LOJ上搬了三道原题给大家考了考,然后大家都在考试就我一个人在划水. SSerxhs 和 ...

  2. Codeforces VP/补题小记 (持续填坑)

    Codeforces VP/补题小记 1149 C. Tree Generator 给你一棵树的括号序列,每次交换两个括号,维护每次交换之后的直径. ​ 考虑括号序列维护树的路径信息和,是将左括号看做 ...

  3. [LeetCode] Binary Tree Paths 二叉树路径

    Given a binary tree, return all root-to-leaf paths. For example, given the following binary tree: 1 ...

  4. [LeetCode] Valid Anagram 验证变位词

    Given two strings s and t, write a function to determine if t is an anagram of s. For example, s = & ...

  5. [LeetCode] Unique Paths II 不同的路径之二

    Follow up for "Unique Paths": Now consider if some obstacles are added to the grids. How m ...

  6. [LeetCode] Unique Paths 不同的路径

    A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The ...

  7. Leetcode Valid Anagram

    Given two strings s and t, write a function to determine if t is an anagram of s. For example,s = &q ...

  8. leetcode : Binary Tree Paths

    Given a binary tree, return all root-to-leaf paths. For example, given the following binary tree: 1 ...

  9. UVA 10564 Paths through the Hourglass[DP 打印]

    UVA - 10564 Paths through the Hourglass 题意: 要求从第一层走到最下面一层,只能往左下或右下走 问有多少条路径之和刚好等于S? 如果有的话,输出字典序最小的路径 ...

随机推荐

  1. PHP-操作json

    输出 json 文件中文处理 <?php $json_array = array(); // 1.转换为json字符串(不自动转换为unicode编码) if (version_compare( ...

  2. tomcat启动控制台报Exception in thread ''main".......“Could not find the main class:.....Bootstrap”问题

    startup.bat文件打开最后end下一行加pause调试,重新启动tomcat,发现配置没问题,但是依然报错,发现是jdk版本问题,jdk1.6无法与tomcat8适配,重新装个1.7版本的jd ...

  3. JDBC常用接口、类介绍

    JDBC常用接口.类介绍 JDBC提供对独立于数据库统一的API,用以执行SQL命令.API常用的类.接口如下: DriverManager 管理JDBC驱动的服务类,主要通过它获取Connectio ...

  4. spring-boot BUG 集锦

    BUG1:  If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. BUG2:使用 ...

  5. Java IO(2)

    关于流的概念 Java 由流来完成具体的IO操作,虽然面对的是不同的外设(网络.鼠标.键盘)IO流使用与全部的外设,在底层Java已经将具体与物理设备交互的细节都处理好了. 流的分类: 从功能上 输入 ...

  6. 利用正则表达式模拟计算器进行字符串的计算实现eval()内置函数功能

    代码感觉有点绕,刚开始学习python,相关知识点还没全部学习到,还请各位大神多多指教 import re # 定义乘法 def mul(string): mul1 = re.search('-?\d ...

  7. iBatis——自动生成DAO层接口提供操作函数(详解)

    iBatis——自动生成DAO层接口提供操作函数(详解) 在使用iBatis进行持久层管理时,发现在使用DAO层的updateByPrimaryKey.updateByPrimaryKeySelect ...

  8. SQL 日期格式化与格式转化

    日期格式化 Select CONVERT(varchar(), GETDATE(), ): :57AM Select CONVERT(varchar(), GETDATE(), ): // Selec ...

  9. AJAX - 实现一个简单的登录验证

    /**Ajax 编写流程 * 1.创建 XHR (XMLHttpRequest)对象 var xmlHttpReq = false; // var xmlHttpReq = ""; ...

  10. vue + element 创建教程

    一.环境准备 node安装 node版本:v10.15.3 node下载地址:https://nodejs.org/zh-cn/ vue-cli安装 全局安装vue-cli npm install - ...