Educational Codeforces Round 127 (Rated for Div. 2) Problem E Preorder

题目
题意
给定一棵完全二叉树,每个结点的值是字符 ‘A’,‘B’ 中的其中一个。
你可以进行以下操作若干次:
选定一个结点u ,对于左孩子l 和右孩子 r,将 u的左孩子置为 r,右孩子置为l (或者认为是交换了两棵子树,这会影响遍历顺序)。
随后遍历这棵树,得到一个先序遍历序列 s。求 s 有多少种不同可能。

思路
首先我们有这样一个感性的认识,有以u为根的树,假设左子树的方案有x种,右子树的方案有y种,假设左右子树不同(即无论怎样交换左右子树表示的字符串都不会相同),那么方案数就是xy,假设左右子树相同方案数就是2xy
所以我们就可以递归地去求了,问题主要在于怎么判断两个子树是否相同.
可能有朋友接触过最小表示法,这里的思路类似,假设左子树代表的字符串为s1,右子树代表的字符串为s2,根节点的字母为u,那么表示的字符串只能是s1+u+s2或者s2+u+s1,但是我们可以总是让字典序比较小的那个状态在前面,这样只要是等价的子树形成的字符串都是相同的.比较的时候只需要直接暴力比较了,对于每一层比较的复杂度是O(n)的,所以总体比较的复杂度是O(nlogn)因为最终答案肯定是2的幂,所以我代码中维护的是2的幂数,当然直接维护结果也是可以的
代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
const int mod=998244353;
char s[maxn];
int f[maxn];
ll qpow(ll a, ll b)//快速幂
{
    ll res = 1;
    while (b)
    {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

string dfs(int u)
{
    if(!s[u<<1])    //判断是否为叶子节点
       return s[u]=='A'?"A":"B";
     string s1=dfs(u<<1),s2=dfs(u<<1|1);//左儿子为2*u,右儿子为2*u+1
    f[u]=f[u<<1]+f[u<<1|1];//先统计以左右两个儿子为根的,有多少种不同方案
    if(s1!=s2)
        f[u]++;//再看如果以当前u为根,翻转一下是否一样,如果不一样,答案+1
    if(s1<s2)
        return s1+s[u]+s2;
    else
        return s2+s[u]+s1;

}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n>>s + 1;
    dfs(1);
    cout<<qpow(2,f[1])<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值