1. 程式人生 > >[洛谷 P1310]表示式的值 --- 表示式樹(中綴轉字尾)+DP

[洛谷 P1310]表示式的值 --- 表示式樹(中綴轉字尾)+DP

傳送門:洛谷 P1310


題目描述

對於1 位二進位制變數定義兩種運算:

這裡寫圖片描述
運算的優先順序是:

先計算括號內的,再計算括號外的。

× × ”運算優先於“ \bigoplus ”運算,即計算表示式時,先計算 ×

× 運算,再計算 \bigoplus 運算。例如:計算表示式 A B ×
C A\bigoplus B×C
時,先計算 B × C B × C ,其結果再與 A
A
\bigoplus 運算。

現給定一個未完成的表示式,例如 + ( ) +(*) ,請你在橫線處填入數字 0 0 或者 1 1 ,請問有多少種填法可以使得表示式的值為 0 0


分析

無論是用棧做還是用樹做,其目的都是理清 D P DP 順序。
注意題目省略了數字(類比吧),自行腦補一下

一.中綴表示式轉字尾表示式

(放在前面強調)對於本題來說,數字要自己補( 0 / 1 0/1 的方案數均為 1 1 )

對於一棵表示式樹而言,葉子節點全是數字,非葉子節點全是操作符

基於手動演算法:

  1. 補全括號: a + b c ( d + e ) ( ( a + ( b c ) ) ( d + e ) ) a+b*c-(d+e) \to ((a+(b*c))-(d+e))

  2. 將運算子放在括號後面: ( ( a + ( b c ) ) ( d + e ) ) ( ( a ( b c ) ) + ( d e ) + ) ((a+(b*c))-(d+e)) \to ((a(bc)* )+ (de)+ )-

  3. 去掉所有括號: ( ( a ( b c ) ) + ( d e ) + ) a b c + d e + ((a(bc)* )+ (de)+ )- \to abc*+de+-

稍加分析改進後(不必加括號),就有了如下演算法:

  1. 判斷兩邊是否有多餘的括號,有的話去掉

  2. 從後往前(正常計算時,左子樹會先計算,所以,對於同級的操作符來說,前面的歸為左子樹),找到優先順序最低且最後面的運算子,以它為父親節點。

  3. 左右子樹遞迴處理

二.DP

令f[i][0/1]表示子樹i的為0/1的方案數

友情提示:注意取模(否則 20 20

if(節點i為 ‘+) 
	f[i][0] = f[lch][0] * f[rch][0];
	f[i][1] = f[lch][0] * f[rch][1] + f[lch][1] * f[rch][0] + f[lch][1] * f[rch][1];
        
if(節點i為 '*' )
	f[i][0] = f[lch][0] * f[rch][0] + f[lch][0] * f[rch][1] + f[lch][1] * f[rch][0];
	f[i][1] = f[lch][1] * f[rch][1];

程式碼

#include <cstdio>
#include <cstdlib>
#include <stack>

#define IL inline

using namespace std;

IL int read()
{
    int sum = 0, k= 1;
    char c = getchar();
    
    for(;'0' > c || c > '9'; c = getchar())
    if(c == '-') k = 0;
    
    for(;'0' <= c && c <= '9'; c = getchar())
        sum = sum * 10 + c - '0';
    return k ? sum : -sum;
}

const int mo(10007);
int n;

char mp[100005];
int to[100005];
stack<int>stk;

IL int turn(char c)
{
    if(c == '+') return 1;
    if(c == '*') return 2;
    return 3;
}

IL int find(int l, int r)
{
    int p = -1;
    for(; r >= l;)
    if(mp[r] == ')')
    {
        r = to[r] - 1;
    }else
    {
        if(p == -1 || turn(mp[r]) < turn(mp[p])) p = r;
        if(mp[p] == '+') return p;
        --r;
    }
    return p;
}

struct node
{
    int x, y;
    IL node(int x_ = 0, int y_ = 0)
    {
        x = x_; y = y_;
    }
};

IL node dfs(int l, int r)
{
    if(l > r) return node(1, 1);

    if(mp[l] == '(' && mp[r] == ')' && to[r] == l) { ++l; --r; }
    if(l > r) return node(1, 1);
    int p = find(l, r);
    
    //printf("%d %c\n", p, mp[p]);
    
    node tmp1 = dfs(l, p - 1), tmp2 = dfs(p + 1, r);
    if(mp[p] == '+') return node( tmp1.x * tmp2.x % mo, (tmp1.x * tmp2.y % mo + tmp1.y * tmp2.x % mo + tmp1.y * tmp2.y % mo) % mo);
    if(mp[p] == '*') return node( (tmp1.x * tmp2.x % mo + tmp1.x * tmp2.y % mo + tmp1.y * tmp2.x % mo) % mo, tmp1.y * tmp2.y % mo);
}

int main()
{
    n = read();
    char c;
    
    for(int i = 1; i <= n; ++i)
    {	
        scanf(" %c", &c);
        mp[i] = c;
        if(c == '(') stk.push(i); else
        if(c == ')') { to[i] = stk.top(); stk.pop(); }
    }

    printf("%d\n", dfs(1, n).x);
    return 0;
}