[CSP-S2019] 括号树

算法

特殊性质

显然链的情况就是括号匹配

因此显然有代码

代码

#include <bits/stdc++.h>
#define int long long
const int MAXN = 5e5 + 20;

int n;
std::string Braket;
int fa[MAXN];

bool Check_Special_Quality()
{
    for (int i = 2; i <= n; i++)
    {
        if (fa[i] != i - 1)
        {
            return false;
        }
    }

    return true;
}

std::stack<int> Pre;
int k[MAXN];
int Sum[MAXN];

int calc()
{
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        // printf("%d ", k[i]);
        ans = ans ^ (i * Sum[i]);
    }

    return ans;
}

void Solve_For_Special_Quality()
{
    for (int i = 1; i <= n; i++)
    {
        if (Braket[i] == '(')
        {
            Pre.push(i);
        }
        else if (Braket[i] == ')')
        {
            if (Pre.empty())
            {
            }
            else
            {
                int Now = Pre.top();
                Pre.pop();
                k[i] = k[Now - 1] + 1;
            }
        }

        Sum[i] = Sum[i - 1] + k[i];
    }

    printf("%lld", calc());
}

signed main()
{

    scanf("%lld", &n);
    std::cin >> Braket;
    Braket = ' ' + Braket;
    for (int i = 2; i <= n; i++)
    {
        scanf("%lld", &fa[i]);
    }

    if (Check_Special_Quality())
    {
        Solve_For_Special_Quality();
        return 0;
    }

    return 0;
}

注意不能用单数组, 因为不能计算反括号对应正括号

正解

显然有树状的递推性质
那么有

\[lst_i = lst_{t - 1} + 1 \rightarrow lst_x = lst_{\text{fa of x}} + 1 \]

代码

#include<bits/stdc++.h>
#define orz 0
#define inf 0x3f3f3f3f
#define ll long long
#define maxn 500005;

using namespace std;

int n;
char c[maxn];
int head[maxn], nxt[maxn], to[maxn], cnt, fa[maxn];
ll lst[maxn], sum[maxn], ans;
int s[maxn], top;

void add_edge(int u, int v)
{
	nxt[++ cnt] = head[u];
	head[u] = cnt;
	to[cnt] = v;
}

void dfs(int x)
{
	int tmp = 0;
	if(c[x] == ')')
	{
		if(top)
		{
			tmp = s[top];
			lst[x] = lst[fa[tmp]] + 1;
			-- top; 
		}
	}
	else if(c[x] == '(') s[++ top] = x; 
	sum[x] = sum[fa[x]] + lst[x]; //如上所述 
	for(int i = head[x]; i; i = nxt[i])
		dfs(to[i]); //递归 
	//回溯复原操作
	if(tmp != 0) s[++ top] = tmp; //不为 0 代表有信息被弹出 
	else if(top) -- top; 
	//为 0 代表没有弹出,如果栈不为空说明一定压入了一个信息,需要弹出这个信息复原 
}

int main()
{
	scanf("%d", &n);
	scanf("%s", c + 1);
	for(int i = 2; i <= n; i ++)
	{
		int f;
		scanf("%d", &f);
		add_edge(f, i);
		fa[i] = f;
	}
	dfs(1);
	for(int i = 1; i <= n; i ++)
		ans ^= sum[i] * (ll)i;
	printf("%lld", ans);
	return orz;
}

ctj 是这样的

总结

打特殊性质记得推下样例

特殊套路 :
dfs 时保持状态稳定

posted @ 2024-10-07 21:15  Yorg  阅读(16)  评论(0编辑  收藏  举报