[codechef] REBIT:准备好每一位(表达式/DP)

Problem

题目地址

Solution

前置知识

中缀表示式序列可以转化成一棵二叉树。发现计算概率可以转化成计数,在二叉树上做一个树形DP就好了。

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
using namespace std;
typedef int LL;
#define int long long
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 1e6+7, mod = 998244353;
int len,tot;
int ls[N],rs[N];
int f[N][4];
char str[N],c[N];
struct Node {
	int x,y;
	Node operator & (const Node &el) const {
		return (Node)<%x&el.x, y&el.y%>;
	}
	Node operator | (const Node &el) const {
		return (Node)<%x|el.x, y|el.y%>;
	}
	Node operator ^ (const Node &el) const {
		return (Node)<%x^el.x, y^el.y%>;
	}
	bool operator < (const Node &el) const {
		return (x==el.x ? y<el.y : x<el.x);
	}
};
map<int,Node> inode;
map<Node,int> acinode;
void Init() {
	inode[0] = (Node)<%0,0%>, inode[1] = (Node)<%1,1%>, inode[2] = (Node)<%0,1%>, inode[3] = (Node)<%1,0%>;
	acinode[(Node)<%0,0%>] = 0, acinode[(Node)<%1,1%>] = 1, acinode[(Node)<%0,1%>] = 2, acinode[(Node)<%1,0%>] = 3;
}
inline void Add(int &x,int y) {
	x = (x+y>mod ? x+y-mod : x+y);
}
void Dfs(int u) {
	int x = ls[u], y = rs[u];
	if(x) {
		Dfs(x); Dfs(y);
	} else return ;
	for(int i=0;i<4;++i) {
		Node ii = inode[i];
		for(int j=0;j<4;++j) {
			Node jj = inode[j];
			if(c[u] == '&') {
				Add(f[u][acinode[ii & jj]], f[x][i]*f[y][j]%mod);
			} else if(c[u] == '|') {
				Add(f[u][acinode[ii | jj]], f[x][i]*f[y][j]%mod);
			} else if(c[u] == '^') {
				Add(f[u][acinode[ii ^ jj]], f[x][i]*f[y][j]%mod);
			}
		}
	}
}
int Pow(int x,int y) {
	int res = 1, base = x;
	while(y) {
		if(y&1) res = res*base%mod; base = base*base%mod; y >>= 1;
	}
	return res;
}
void work() {
	for(int i=1;i<=tot;++i) {
		c[i] = ls[i] = rs[i] = 0;
		for(int j=0;j<4;++j) f[i][j] = 0;
	}
	tot = 0;
	scanf("%s",str+1); len = strlen(str+1)
	stack<char> sta; stack<int> num;
	for(int i=0;i<=len+1;++i) {
		if(str[i] == '(') {
			sta.push(str[i]);
		} else if(str[i] == ')') {
			while(sta.top() != '(') {
				char opt = sta.top(); sta.pop();
				int x = num.top(); num.pop();
				int y = num.top(); num.pop();
				c[++tot] = opt; ls[tot] = y, rs[tot] = x;
				num.push(tot);
			}
			sta.pop();
		} else if(str[i] == '#') {
			c[++tot] = str[i]; num.push(tot);
		} else {
			sta.push(str[i]);
		}
	}
	for(int i=1;i<=tot;++i) {
		if(!ls[i]) {
			for(int j=0;j<4;++j) f[i][j] = 1;
		}
	}
	Dfs(tot);
	int sum = 0;
	for(int i=0;i<4;++i) Add(sum, f[tot][i]);
	int inv = Pow(sum,mod-2);
	for(int i=0;i<4;++i) printf("%lld ",f[tot][i]*inv%mod);
	puts("");
}
signed main()
{
	Init();
	int T = read();
	while(T--) work();
	return 0;
}
/*
2
#
(#&#)
()

748683265 748683265 748683265 748683265
436731905 935854081 811073537 811073537
*/

Summary

  • 中缀表示式序列可以转化成一棵二叉树。从而将此类问题转化为树上问题。
posted @ 2020-11-17 09:15  基地AI  阅读(198)  评论(0编辑  收藏  举报