原題鏈接 https://www.luogu.org/problemnew/show/P1310
water_lift 一波講解,然后我們就會了這個題,然后我們就要寫博客啦QwQ~



這是個布爾表達式基計數問題。
這個其實⊕就是“|”或運算,×就是“&”與運算。
我們將這個式子通俗得看成:x&y 和 x|y
我們設x0是使x為0的方案數,x1是使x為1的方案數;y0是使y為0的方案數,y1是使y為1的方案數;
使x&y為1的方案數?為0的方案數?
使x|y為1的方案數?為0的方案數?
不難發現:
使x&y為1,那么x和y都要為1,所以方案數為x1∗y1。
使x&y為0,那么x和y不都為1,所以方案數為x1∗y0+x0∗y1+x0∗y0。
使x|y為1的方案數為x1∗y1+x0∗y1+x1∗y0,為0的方案數為x0∗y0。
然后我們發現給出的式子一個特別煩的東東就是: 有括號
為了去掉括號,我們可以考慮將題目輸入的中綴表達式給它換成后綴表達式(后綴表達式的好處就是式子中沒有括號!)
腫么換呢?這確實是一個難題,還好我們找到了這樣一個方法(由於太蒟還不會證正確性):
遍歷中綴表達式:
-
遇到數字,直接放入答案序列
-
遇到左括號,入棧
- 遇到右括號,把棧頂到上一個左括號的元素依次出棧並放入答案序列
- 遇到乘號,入棧
- 遇到加號,從棧頂開始彈出這段連續的乘號,並放入答案序列,最后加號入棧
-
最后把棧里剩下的元素依次放入答案序列
為什么是正確的呢?貼一下water_lift的模擬過程(想要更豐富的展現?請看water_lift的博客):



那么有了后綴表達式,這么求值?
這個想必大家都會了,我們用棧就能輕松搞定qwq:
遇到數字入棧,遇到運算符號就取出棧頂的兩個元素,將它們進行該運算符的運算后再入棧。
The last question:
題目要求的是使得表達式為0的方案數,那么在哪填數字?
看一下樣例:
+(*)
很顯然就是在這幾個標紅的位置填數字啦:
_+(_*_)
So,我們可以總結一下在哪里填數字: 式子最前面一定要填個數字(你見哪個式子是符號打頭?),然后“+”和“×”后面要填一個數字!
到這里咱們就可以食用代碼啦:
#include <bits/stdc++.h> using namespace std; stack<char> fh; //中綴表達式轉后綴表達式的時候所要用到的存符號的棧 stack<int> zero; //一個存使表達式為0的方案數的棧 stack<int> one; //一個存使表達式為1的方案數的棧 string houxu; //轉化后的后綴表達式 char ch[100001]; //存輸入的字符串 int n,l0,l1,r0,r1; //這里l0就是上文的x0,l1是x1,r0是y0,r1是y1 const int mod=10007; int main() { scanf("%d",&n); scanf("%s",ch+1); //ch+1是讓它從下標為1開始讀的 houxu.push_back('n'); //后綴表達式的最前頭要有個數字,用字符'n'來表示這里應該填數字 for(int i=1;i<=n;i++) { if(ch[i]=='('||ch[i]=='*') //如果是'('或'*',正常入符號棧 { fh.push(ch[i]); } if(ch[i]=='+') //如果是'+',要把符號棧棧頂的'*' 都放入后綴表達式的后面 { while(!fh.empty()&&fh.top()=='*') { houxu.push_back(fh.top()); fh.pop(); } fh.push(ch[i]); } if(ch[i]==')') //如果是')',則要把'('和')'之間的符號放入后綴表達式的后面 { while(fh.top()!='(') { houxu.push_back(fh.top()); fh.pop(); } fh.pop(); //彈出左括號 } if(ch[i]!='('&&ch[i]!=')') houxu.push_back('n'); //如果不是括號,也就是說如果是'+'或'*',那么就要在后面填一個數字 } while(!fh.empty()) //將符號棧中剩余的符號全部放入后綴表達式 { houxu.push_back(fh.top()); fh.pop(); } for(int i=0;i<houxu.size();i++) //可以看成是后綴表達式求值 { char c=houxu[i]; if(c=='n') //如果這里該填數字 { zero.push(1); //該數字填0的方案有1種 one.push(1); //該數字填1的方案有1種 } else //如果是符號 { l0=zero.top();zero.pop(); //彈出一個使l為0的方案數 l1=one.top();one.pop(); //彈出一個使l為1的方案數 r0=zero.top();zero.pop(); //彈出一個使r為0的方案數 r1=one.top();one.pop(); //彈出一個使r為1的方案數 if(c=='*') //&運算 { one.push((l1*r1%mod)%mod); //兩個都為1 zero.push((l0*r0%mod+l1*r0%mod+l0*r1%mod)%mod); //不都為1 } else //|運算 { zero.push((l0*r0%mod)%mod); //兩個都為0 one.push((l0*r1%mod+l1*r0%mod+l1*r1%mod)%mod); //不都為0 } } } printf("%d",zero.top()%mod); //最后剩下的就是使整個表達式為0的方案數 return 0; }
