[ZPG TEST 114] 括号匹配【树分治 点分治 括号序列】
- 1. 括号匹配
有一棵树,每个节点上都有一个括号(左括号或者右括号)。有多少个有序点对(u, v)从u到v的路径上的节点构成的字符串是一个合法的括号匹配?(我们称这样的点对是合法的)
输入格式
第一行是一个整数n,表示节点个数
第二行是一个字符串,由(和)组成,第i个字符表示第i个节点的括号。
接下来n-1行,每行两个整数u, v 表示u与v之间有一条边。
输出格式
输出一个整数,合法的点对数量。
|
输入样例 |
输出样例 |
|
4 ()() 1 2 2 3 3 4 |
4 |
数据范围和约定
30%的数据:n <= 2000
100%的数据:n <= 100000
困扰了我多年(其实是多个月),终于于今日解决了!(我才不会说克罗地亚国赛最后一题是原题呢= =)如果没做过树分治的话还是先做一下poj1741感受一下叭。
先把随题配送的题解贴出来:
“这道题是很常规的树分治算法。分治之后考虑经过根的所有链,将链上的所有能匹配的括号合并,那么只有其中一边由若干左括号和另外一边的若干右括号的情况能构成合法的括号匹配。统计括号数即可。”
写的非常简洁,然而最初根本看不懂。事实上是这样子的,首先考虑当前子树,一条合法的括号序列链可以经过该子树的根节点,也可以不经过,对于后者可以递归求解,重点是看前者。
一条链经过这个根节点,那么这条链一定是长成这个样子的:
根
/ \
/ \
/ \
/ \
姑且把根旁边的两条支叫做左支与右支(注意原树并不一定是二叉树),那么这个括号序列一定是由左支的一些已经匹配好的括号序列,与右支的一些已经匹配好的括号序列,与左支还未匹配的 '(' 与右支未匹配的 ')' 。当然也有可能是由左支的一些已经匹配好的括号序列,与右支的一些已经匹配好的括号序列,与右支还未匹配的 '(' 与左支未匹配的 ')' 。这里不妨考虑前者。现在关键就是让那些分在两支的未匹配的括号得以匹配。具体的话还是看代码,最关键的是get_data()函数。还需要小注意一下78行的注释内容。
#include <cstdio>
#include <cstring>
#include <algorithm> const int maxn = 100005, LEFT = 0, RIGHT = 1; int n, head[maxn], to[maxn << 1], next[maxn << 1], lb, a[maxn], mp[128];
int t1, t2, siz[maxn], cnt[2][maxn], avail_len[2][maxn], idx[2], sum[2][maxn];
long long ans;
char ch, book[maxn]; inline void ist(int aa, int ss) {
to[lb] = ss;
next[lb] = head[aa];
head[aa] = lb;
++lb;
}
void get_siz(int r, int p) {
siz[r] = 1;
for (int j = head[r]; j != -1; j = next[j]) {
if (!book[to[j]] && to[j] != p) {
get_siz(to[j], r);
siz[r] += siz[to[j]];
}
}
}
void fnd_zx(int r, int tot_node, int p, int & real_r, int & mn) {
int mx = 0;
for (int j = head[r]; j != -1; j = next[j]) {
if (!book[to[j]] && to[j] != p) {
fnd_zx(to[j], tot_node, r, real_r, mn);
mx = std::max(mx, siz[to[j]]);
}
}
mx = std::max(mx, tot_node - siz[r]);
if (mn > mx) {
mn = mx;
real_r = r;
}
}
void get_data(int r, int p, int dir, int avail, int s) {
// r是当前子树的根节点,p是其父亲,dir是你想要找的可供与另一支匹配的括号类型,
// avail表示目前可供匹配的个数,s有点玄 ,只有当s为零的时候,这个括号序列才是
// 可以用来与另一支匹配的,用语言不好表达,简单模拟一下程序的45~61行就懂了
if (a[r] == dir) {
if (s) {
--s;
}
else {
++avail;
}
}
else {
++s;
}
if (!s) {
if (!cnt[dir][avail]) {
avail_len[dir][idx[dir]++] = avail;
}
++cnt[dir][avail];
}
for (int j = head[r]; j != -1; j = next[j]) {
if (!book[to[j]] && to[j] != p) {
get_data(to[j], r, dir, avail, s);
}
}
}
void slove(int fake_r) {
int real_r = -666, mn = 2147483647, tem;
get_siz(fake_r, 0);
fnd_zx(fake_r, siz[fake_r], 0, real_r, mn);
book[real_r] = 1;
for (int j = head[real_r]; j != -1; j = next[j]) {
if (!book[to[j]]) {
slove(to[j]);
}
}
int mx_avail = 1; // mx_avail must be initialized to 1! 0 is NOT OK!
for (int j = head[real_r]; j != -1; j = next[j]) {
if (!book[to[j]]) {
if (a[real_r] == LEFT) {
get_data(to[j], real_r, LEFT, 1, 0);
get_data(to[j], real_r, RIGHT, 0, 0);
}
else {
get_data(to[j], real_r, RIGHT, 1, 0);
get_data(to[j], real_r, LEFT, 0, 0);
}
for (int dir = 0; dir < 2; ++dir) {
for (int k = 0; k < idx[dir]; ++k) {
tem = avail_len[dir][k];
mx_avail = std::max(mx_avail, tem);
ans -= (long long)cnt[dir][tem] * cnt[dir ^ 1][tem];
sum[dir][tem] += cnt[dir][tem];
cnt[dir][tem] = 0;
}
idx[dir] = 0;
}
}
} ++sum[a[real_r]][1];
for (int i = 0; i <= mx_avail; ++i) {
ans += (long long)sum[0][i] * sum[1][i];
sum[0][i] = 0;
sum[1][i] = 0;
} book[real_r] = 0;
} int main(void) {
freopen("bracket.in", "r", stdin);
freopen("bracket.out", "w", stdout);
memset(head, -1, sizeof head);
memset(next, -1, sizeof next);
mp['('] = LEFT;
mp[')'] = RIGHT;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
while ((ch = getchar()) < '(');
a[i] = mp[(int)ch];
}
for (int i = 1; i < n; ++i) {
scanf("%d%d", &t1, &t2);
ist(t1, t2);
ist(t2, t1);
} slove(1);
printf("%I64d\n", ans);
return 0;
}
[ZPG TEST 114] 括号匹配【树分治 点分治 括号序列】的更多相关文章
- C. Serval and Parenthesis Sequence 【括号匹配】 Codeforces Round #551 (Div. 2)
冲鸭,去刷题:http://codeforces.com/contest/1153/problem/C C. Serval and Parenthesis Sequence time limit pe ...
- CF308C-Sereja and Brackets-(线段树+括号匹配)
题意:给出一段括号,多次询问某个区间内能匹配多少括号. 题解:线段树,结构体三个属性,多余的左括号l,多余的右括号r,能够匹配的括号数val. 当前结点的val=左儿子的val+右儿子的val+min ...
- Sereja and Brackets(括号匹配)
Description Sereja has a bracket sequence s1, s2, ..., sn, or, in other words, a string s of length ...
- POJ1741——Tree(树的点分治)
1 /* *********************************************** 2 Author :kuangbin 3 Created Time :2013-11-17 1 ...
- 括号匹配 区间DP (经典)
描述给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来 ...
- HDU4812 D Tree(树的点分治)
题目大概说给一棵有点权的树,输出字典序最小的点对,使这两点间路径上点权的乘积模1000003的结果为k. 树的点分治搞了.因为是点权过根的两条路径的LCA会被重复统计,而注意到1000003是质数,所 ...
- YTU 3003: 括号匹配(栈和队列)
3003: 括号匹配(栈和队列) 时间限制: 1 Sec 内存限制: 128 MB 提交: 2 解决: 2 [提交][状态][讨论版] 题目描述 假设一个表达式中只允许包含三种括号:圆括号&quo ...
- C++,利用链式栈实现括号匹配,界面友好,操作方便,运行流畅
#include<iostream> #include<string> using namespace std; struct Node { char ch; Node* ne ...
- [原]NYOJ 括号匹配系列2,5
本文出自:http://blog.csdn.net/svitter 括号匹配一:http://acm.nyist.net/JudgeOnline/problem.php?pid=2 括号匹配二:htt ...
随机推荐
- 【USACO OPEN 10】hop
奶牛们正在回味童年,玩一个类似跳格子的游戏,在这个游戏里,奶牛们在草地上画了一行N个格子,(3 <=N <= 250,000),编号为1..N. 就像任何一个好游戏一样,这样的跳格子游戏也 ...
- JavaScript学习第四天
1.自定义属性,使用好索引值 例子: 下面有一段js代码: <script> window.onload = function(){ var oBtn = document.getElem ...
- fatal error C1902: 程序数据库管理器不匹配;请检查安装解决
http://blog.sina.com.cn/s/blog_9f4bc8e301015uhz.html 1.错误提示:VS2008编译错误fatal error C1902: 程序数据库管理器不匹配 ...
- 浏览器端JS导出EXCEL——001
<script src="https://rawgithub.com/eligrey/FileSaver.js/master/FileSaver.js" type=" ...
- Unable to resolve target 'android-16'
Just now when I imported the "android-support-v7-appcompat" to ADT,the console pointed out ...
- vmware 克隆linux虚拟机后的网卡修改方法
VMware虚拟机安装好CentOS6.5系统后,纯净的系统多克隆几份出来方便后期做试验.克隆步骤很简单,克隆后出现的问题是克隆后的网卡MAC地址和原系统MAC地址一样,在局域网内会有冲突,需要更改克 ...
- [CROATIAN2009] OTOCI
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=1180 [算法] 动态树维护森林连通性 时间复杂度 : O(NlogN ^ 2) [代 ...
- SecureCRT远程连接虚拟机CentOS的三种方式
当在VMware虚拟机中将CentOS安装成功后,会在win7系统中模拟出两个虚拟网卡:VMnet1和VMnet8,我们来查看一下,点击“控制面板—>查看网络状态和任务—>更改适配器设置” ...
- CS231n 2016 通关 第五章 Training NN Part1
在上一次总结中,总结了NN的基本结构. 接下来的几次课,对一些具体细节进行讲解. 比如激活函数.参数初始化.参数更新等等. ====================================== ...
- React-Native 基本环境的搭建
看了一些RN资料,仅仅了解到人们对 RN(以下简称 React_native 框架) 的使用描述以及评价,就觉得RN是一个很不错的框架,值得学习.今天就开始写我的学习记录,也给大家分享一下.下面进入正 ...