Brackets
序言
可以先参考题目: 最长括号匹配_NOI导刊2009提高(1)
正文
首先, 从最简单的入手, 也就是一条链.
这里要求的是 "括号串中有多少个互不相同的子串是合法括号串", 首先, 可以借鉴一下序言中那道题目的思路.
用 dp_length[i] 表示 "括号串以第 i 位结尾的最长合法序列长度", 那么可以得到:
if (bracket[i] == ')') //')'
{
if (bracket[i - 1 - dp_length[i - 1]] == '(')
dp_length[i] = dp_length[i - 1] + 2 + dp_length[i - 2 - dp_length[i - 1]];
} Why? 若 A, B 是括号匹配的字符串, 那么对于串 "A(B)"(当前位为 ')'), dp_length[i] = dp_length[B] + dp_length[A] + 2.
接下来, 看看 "有多少个互不相同的子串是合法括号串".
用 dp_sum[i] 表示 "括号串以第 i 位结尾的字串中有多少个互不相同的子串是合法括号串", 那么, 不难得到:
if (bracket[i] == ')') //')'
{
if (bracket[i - 1 - dp_length[i - 1]] == '(')
{
dp_length[i] = dp_length[i - 1] + 2 + dp_length[i - 2 - dp_length[i - 1]];
dp_count[i] = dp_count[i] + 1 + dp_count[i - 2 - dp_length[i - 1]];
}
}
dp_sum[i] = dp_sum[i - 1] + dp_count[i]; dp_count[] 的推导也是同理.
至此, 我们已经解决了链的情况. 于是, 我们发现, 通过 dfs 可以将树转化成链!
然而...Time Limit Exceeded...
But why? 原因在于每找到一条链, 都要从根节点开始求解, 而各条链又有很大一部分是重复的.
比如这棵树:
在我们计算了 {1, 2, 3, 5} 这条链后, 根据 dfs 序, 下一条链为 {1, 2, 3, 6}, 其中, 链 {1, 2, 3} 是重复的. 所以只用从 {6} 开始计算即可.
完整代码
注意, 这里dp_sum[], dp_count[], temp[] 都要用 long long 类型, 否则最后 4 个测试点过不去.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int VNUM = 5e5 + 5;
const int ENUM = 5e5 + 5;
struct Edge
{
int end;
int next;
};
Edge edge[ENUM << 1];
int head[VNUM];
int edgestamp;
int dp_length[VNUM];
long long dp_count[VNUM];
long long dp_sum[VNUM];
int id[VNUM]; //dfs链第 i 位为节点 id[i]
char sequence[VNUM]; //dfs链
char bracket[VNUM]; //原括号串
long long temp[VNUM]; //si 中有多少个互不相同的子串是合法括号串
long long ans;
int lower; //记录重复位置
void addEdge(int u, int v);
void dfs(int depth, int u, int father); //当前深度, 当前节点, 父节点
void dynamic(int upper);
int main()
{
int n;
memset(bracket, '#', sizeof(bracket));
scanf("%d", &n);
scanf("%s", bracket + 1);
for (register int i = 2; i <= n; i++)
{
int u;
scanf("%d", &u);
addEdge(u, i);
addEdge(i, u);
}
lower = 0; //开始时, 没有重复
dfs(1, 1, 0);
ans = 0;
for (register int i = 1; i <= n; i++) //异或和
ans ^= (temp[i] * i);
printf("%lld\n", ans);
return 0;
}
void addEdge(int u, int v)
{
edge[++edgestamp].end = v;
edge[edgestamp].next = head[u];
head[u] = edgestamp;
}
void dfs(int depth, int u, int father)
{
bool isLeaf = true;
sequence[depth] = bracket[u];
id[depth] = u;
for (register int i = head[u]; i != 0; i = edge[i].next)
{
int v = edge[i].end;
if (v == father)
continue;
isLeaf = false;
dfs(depth + 1, v, u);
}
if (isLeaf) //是叶节点, 说明找到了一条链
dynamic(depth);
lower--; //回溯, 重复位置上移
}
void dynamic(int upper)
{
for (register int i = lower + 1; i <= upper; i++) //重复部分保留
dp_length[i] = dp_count[i] = dp_sum[i] = 0;
for (register int i = lower + 1; i <= upper; i++)
{
if (sequence[i] == ')') //')'
{
if (sequence[i - 1 - dp_length[i - 1]] == '(')
{
dp_length[i] = dp_length[i - 1] + 2 + dp_length[i - 2 - dp_length[i - 1]];
dp_count[i] = dp_count[i] + 1 + dp_count[i - 2 - dp_length[i - 1]];
}
}
dp_sum[i] = dp_sum[i - 1] + dp_count[i];
temp[id[i]] = dp_sum[i]; //一个点只会被访问一次
}
lower = upper; //修改重复位置
} 
京公网安备 11010502036488号