P7727 风暴之眼 Eye of the Storm (树形 DP)

谨 以 此 文 表 达 笔 者 个 人 观 点 , 如 有 冒 犯 官 解 , 可 在 评 论 区 诉 说 _{^{_{谨以此文表达笔者个人观点,如有冒犯官解,可在评论区诉说}}}

题面

原题

天体风暴中的气象瞬息万变。

风暴中的道路构成一棵 n n n 个结点的无根树,第 i i i 个结点有初始权值 w i w_i wi w i w_i wi 0 0 0 1 1 1)和类型 t i t_i ti

结点的类型分为两种: AND \texttt{AND} AND 型结点和 OR \texttt{OR} OR 型结点。

对于 AND \texttt{AND} AND 型结点,每一秒结束后它的权值将变为它与它所有邻居上一秒权值的 AND \texttt{AND} AND

对于 OR \texttt{OR} OR 型结点,每一秒结束后它的权值将变为它与它所有邻居上一秒权值的 OR \texttt{OR} OR

现在,已知从某一时刻起,所有结点的权值都不再发生任何变化,将此时点 i i i 的权值称为 a i a_i ai

现不知每个点的初始权值和类型,只知道最终每个点的权值 a i a_i ai,求出有多少种可能的初始权值和类型的组合,答案对 998244353 998244353 998244353 取模。

2 ≤ n ≤ 2 × 1 0 5    ,    1000   m s    ,    256   M B 2\leq n\leq2\times10^5~~,~~1000\,{\tt ms}~~,~~256\,{\tt MB} 2n2×105  ,  1000ms  ,  256MB .

题解

我们可以发现, A N D \tt AND AND 类型一但接触到 0,就会保持 0 永久不变, O R \tt OR OR 类型一旦接触到 1,就会保持 1 永久不变。所以,最终的情况,一定是由权值为 1 的 O R \tt OR OR 、权值为 0 的 A N D \tt AND AND 、四周全是 1 的 A N D \tt AND AND 和 四周全是 0 的 O R \tt OR OR 组成。

我讲的尽量形象一点吧。


我们先看 0 和 1 的交界处,它们比较特别。

这些地方能一直保持这种 “剑拔弩张” 的状态,想必交界处的两个点不简单。简单假设推理一下就会发现,边界处的 1 一定是 O R \tt OR OR 形,因为它们是 “亲 1” 的,能守其 1,义不变 0,如果是 A N D \tt AND AND 形就守不住 1 了,和紧挨着的 0 只用接触一回合,就永远变成 0 了。同理,0 那边一定是 A N D \tt AND AND 形的 0 了。

我们不妨把这两种结点称之为 “哨兵”,每一个 1 的连通块、或者 0 的连通块边上(边上指的是0,1交界处)都必须有哨兵把守。当然,这并不代表连通块内部就没有哨兵了。

类似的,1-连通块内的 A N D \tt AND AND 、0-连通块内的 O R \tt OR OR 这两种朝三暮四的结点,不妨把它们称之为 “庶民” 。

一开始,并不一定每个哨兵都是自己应该的权值(1-连通块内为 1,0-连通块内为 0),它们可能 “未激活”(呈现为敌方的权值),一个未激活的哨兵是可以被身边的 “亲和权值” 给感染、然后激活的,这其中包括身边的激活的友方哨兵(出于呼唤)或未激活的敌方哨兵(出于警觉)。

但是,庶民的摇摆不定就决定了,任何时刻,庶民和 “敌对势力” 或者未激活的友方哨兵(毕竟带有敌方的权值)中间都必须有激活的哨兵相隔。


这么想,那么最初就可能是这样:每个连通块内有若干个小块,这些小块周围是一圈激活的哨兵,内部是哨兵或庶民,连通块内除了这些小块以外的地方,全是未激活的哨兵。随着时间推移,未激活的哨兵渐渐全部激活,最终形成输入中的模样。

但对于每个连通块,还有一种情况:一开始就全是未激活的哨兵!庶民不识抬举,要他们何用 这种情况,连通块四周一定得有至少一个敌方的未激活哨兵相邻才行,不然会被围剿死 不然就全都激活不了了。


好,接下来,就可以着手设计 DP 了。

同权值的连通块一开始是已知的。每个点最初的状态有三种 :未激活的哨兵,激活的哨兵,庶民。分别用 1,2,3 型表示吧,我们设计 d p 1 [ i ] dp1[i] dp1[i] d p 2 [ i ] dp2[i] dp2[i] d p 3 [ i ] dp3[i] dp3[i] 表示结点 i i i 先不考虑父亲的情况下为 1、2、3 型时, i i i 子树的方案数。

但是对于一开始就全是未激活哨兵的情况不好处理,于是我们就令 d p 1 [ i ] [ 1 ] dp1[i][1] dp1[i][1] 为原先的 d p 1 [ i ] dp1[i] dp1[i] ,令 d p 1 [ i ] [ 0 ] dp1[i][0] dp1[i][0] i i i 结点所在连通块(暂不考虑其父亲)全是未激活哨兵、并被围剿死的情况下,子树的方案数,也就是不合法的方案数。

这四者初始值都为 1,因为要做乘法。最后 d p 1 [ i ] [ 1 ] dp1[i][1] dp1[i][1] 要减去 d p 1 [ i ] [ 0 ] dp1[i][0] dp1[i][0] ,即减去不合法。

如果扫描到某个儿子 j j j,与自己在同一个连通块内( a i = a j a_i=a_j ai=aj),那么:

  • d p 1 [ i ] [ 0 ] ← d p 1 [ i ] [ 0 ] ∗ d p 1 [ j ] [ 0 ] dp1[i][0]\leftarrow dp1[i][0]*dp1[j][0] dp1[i][0]dp1[i][0]dp1[j][0] ,这个很明显,儿子也得被围剿才行。
  • d p 1 [ i ] [ 1 ] ← d p 1 [ i ] [ 1 ] ∗ ( d p 1 [ j ] [ 0 ] + d p 1 [ j ] [ 1 ] + d p 2 [ j ] ) dp1[i][1]\leftarrow dp1[i][1]*(dp1[j][0]+dp1[j][1]+dp2[j]) dp1[i][1]dp1[i][1](dp1[j][0]+dp1[j][1]+dp2[j]) ,这是还未减去不合法情况下的 d p 1 [ i ] [ 1 ] dp1[i][1] dp1[i][1] ,所以当然要兼容并包,同时未激活的哨兵可以紧挨着激活的哨兵,所以要算上 d p 2 [ j ] dp2[j] dp2[j]
  • d p 2 [ i ] ← d p 2 [ i ] ∗ ( d p 1 [ j ] [ 0 ] + d p 1 [ j ] [ 1 ] + d p 2 [ j ] + d p 3 [ j ] ) dp2[i]\leftarrow dp2[i]*(dp1[j][0]+dp1[j][1]+dp2[j]+dp3[j]) dp2[i]dp2[i](dp1[j][0]+dp1[j][1]+dp2[j]+dp3[j]) ,已经激活的哨兵在连通块内,说明不符合“全是未激活哨兵” 这种假设了,儿子认为的被围剿死的方案 ( d p 1 [ j ] [ 0 ] dp1[j][0] dp1[j][0]),到父亲这儿又行了。同时,激活的哨兵身边什么人都可以有,自然就全盘转移过来。
  • d p 3 [ i ] ← d p 3 [ i ] ∗ ( d p 2 [ j ] + d p 3 [ j ] ) dp3[i]\leftarrow dp3[i]*(dp2[j]+dp3[j]) dp3[i]dp3[i](dp2[j]+dp3[j]) ,庶民身边只能是庶民或激活的哨兵,这个不必细说。

如果扫描到某个儿子 j j j 与自己在不同连通块的话( a i ≠ a j a_i\not=a_j ai=aj):

  • d p 1 [ i ] [ 0 ] ← d p 1 [ i ] [ 0 ] ∗ d p 2 [ j ] dp1[i][0]\leftarrow dp1[i][0]*dp2[j] dp1[i][0]dp1[i][0]dp2[j] ,既然被对方围剿,那就是和激活的敌方哨兵相邻了。
  • d p 1 [ i ] [ 1 ] ← d p 1 [ i ] [ 1 ] ∗ ( d p 1 [ j ] [ 0 ] + d p 1 [ j ] [ 1 ] + d p 2 [ j ] ) dp1[i][1]\leftarrow dp1[i][1]*(dp1[j][0]+dp1[j][1]+dp2[j]) dp1[i][1]dp1[i][1](dp1[j][0]+dp1[j][1]+dp2[j]) ,对方可以是激活或未激活的哨兵,这个不用说。主要是 d p 1 [ j ] [ 0 ] dp1[j][0] dp1[j][0] ,由于父亲是未激活的哨兵,儿子认为的被围剿死的方案,因父亲放了生路,又行了。
  • d p 2 [ i ] ← d p 2 [ i ] ∗ ( d p 1 [ j ] [ 1 ] + d p 2 [ j ] ) dp2[i]\leftarrow dp2[i]*(dp1[j][1]+dp2[j]) dp2[i]dp2[i](dp1[j][1]+dp2[j]) ,激活的哨兵,儿子被围剿就是被围剿,和上面相比,不能转移过来。
  • d p 3 [ i ] ← 0 dp3[i]\leftarrow 0 dp3[i]0 ,庶民怎么能跑到边境呢?直接清零。

最后再来一个 d p 1 [ i ] [ 1 ] ← ( d p 1 [ i ] [ 1 ] − d p 1 [ i ] [ 0 ] ) dp1[i][1]\leftarrow (dp1[i][1]-dp1[i][0]) dp1[i][1](dp1[i][1]dp1[i][0])

至此,转移就理清了,最终答案是 d p 1 [ R o o t ] [ 1 ] + d p 2 [ R o o t ] + d p 3 [ R o o t ] dp1[Root][1]+dp2[Root]+dp3[Root] dp1[Root][1]+dp2[Root]+dp3[Root]。时间复杂度 O ( n ) O(n) O(n)

CODE

精炼的代码

#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define ENDL putchar('\n')
#define LL long long
#define DB double
#define lowbit(x) ((-x) & (x))
LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f * x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k;
vector<int> g[MAXN];
int inv[MAXN];
int a[MAXN];
int dp1[MAXN][2],dp2[MAXN],dp3[MAXN]; // 1,2,3
void dfs(int x,int ff) {
	dp1[x][0] = dp2[x] = dp3[x] = 1;
	dp1[x][1] = 1;
	for(int i = 0;i < (int)g[x].size();i ++) {
		int y = g[x][i];
		if(y != ff) {
			dfs(y,x);
			if(a[y] == a[x]) {				
				dp1[x][1] = (0ll+dp1[y][0] + dp1[y][1] + dp2[y]) % MOD *1ll* dp1[x][1] % MOD;
				dp1[x][0] = dp1[y][0] *1ll* dp1[x][0] % MOD;
				dp2[x] = (0ll+dp1[y][0]+dp1[y][1] + dp3[y] + dp2[y]) % MOD *1ll* dp2[x] % MOD;
				dp3[x] = (dp2[y] + dp3[y]) % MOD *1ll* dp3[x] % MOD;
			}
			else {
				dp1[x][1] = (0ll+dp1[y][1] + dp1[y][0] + dp2[y]) % MOD *1ll* dp1[x][1] % MOD;
				dp1[x][0] = (dp2[y]) % MOD *1ll* dp1[x][0] % MOD;
				dp2[x] = (dp1[y][1] + dp2[y]) % MOD *1ll* dp2[x] % MOD;
				dp3[x] = 0;
			}
		}
	}
	(dp1[x][1] += MOD-dp1[x][0]) %= MOD;
	return ;
}
int main() {
	n = read();
	for(int i = 1;i <= n;i ++) {
		a[i] = read();
	}
	inv[0] = inv[1] = 1;
	for(int i = 2;i <= n;i ++) {
		inv[i] = (MOD-inv[MOD % i]) *1ll* (MOD/i) % MOD;
	}
	for(int i = 1;i < n;i ++) {
		s = read();o = read();
		g[s].push_back(o);
		g[o].push_back(s);
	}
	dfs(1,0);
	int ans = (0ll+dp1[1][1]+dp2[1]+dp3[1]) % MOD;
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-07-15 22:11  DD_XYX  阅读(40)  评论(0编辑  收藏  举报