Luogu P8815 [CSP-J 2022] 逻辑表达式 题解 [ 绿 ] [ 栈 ] [ 表达式树 ] [ DP ]

逻辑表达式:表达式树裸题。

一个显然的思路是对式子建表达式树,然后树形 DP 求解。

但是这个做法太麻烦了,于是考虑不建树,直接在栈上做。因为每次操作都相当于树形 DP 上合并两个节点的 DP 值,所以数字栈里要记录三个值:结果、和短路、或短路。其余正常做表达式处理即可。

答案即为数字栈中最后留下来的数。

时间复杂度 \(O(n)\)

注意表达式求值的细节:

  • 遇到运算符时,比自己优先级更大的要先处理。
  • 左括号优先级最低,但是入栈的时候不需要进行任何处理。
  • 右括号的时候,要一直弹栈直到左括号。然后把左括号出栈
  • 开头最好加一个左括号,结尾再加一个右括号将所有数字处理完。
  • 加法优先级为 \(1\),乘法优先级为 \(2\)
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
using pii=pair<int,pi>;
char c;
stack<int>op;
stack<pii>num;
void work()
{
    int opt=op.top();
    op.pop();
    pii y=num.top();
    num.pop();
    pii x=num.top();
    num.pop();
    pii res;
    if(opt==1)
        if(x.fi==1)
            res={1,{x.se.fi,x.se.se+1}};
        else
            res={(x.fi|y.fi),{x.se.fi+y.se.fi,x.se.se+y.se.se}};
    else
        if(x.fi==0)
            res={0,{x.se.fi+1,x.se.se}};
        else
            res={(x.fi&y.fi),{x.se.fi+y.se.fi,x.se.se+y.se.se}};
    num.push(res);
}
int main()
{
    //freopen("sample.in","r",stdin);
    //freopen("sample.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    op.push(0);
    while(cin>>c)
    {
        if(c=='0'||c=='1')
            num.push({c-'0',{0,0}});
        else if(c=='|')
        {
            while(op.top()>=1)work();
            op.push(1);
        }
        else if(c=='&')
        {
            while(op.top()>=2)work();
            op.push(2);
        }
        else if(c=='(')
            op.push(0);
        else
        {
            while(op.top()!=0)work();
            op.pop();
        }
    }
    while(op.top()!=0)work();
    op.pop();
    pii ans=num.top();
    cout<<ans.fi<<'\n'<<ans.se.fi<<" "<<ans.se.se;
    return 0;
}
posted @ 2025-06-15 20:58  KS_Fszha  阅读(14)  评论(0)    收藏  举报