Loading

多校A层冲刺NOIP2024模拟赛03 -- T4 量子隧穿问题

多校A层冲刺NOIP2024模拟赛03 -- T4 量子隧穿问题

$$HZOI$$

感觉是这两天最有意义的题吧。

\(n\) 句话题意

我是巴甫洛夫的狗,我又重生了,重生在薛定谔的家里。

薛定谔是抖S,于是给我铃声。我开始狂跑不止。

为什么没流口水没删除我

给定 \(n\) 个点,对于 \(i\) 存在一条外向连的单向边。对于每个点,存在三种状态,有人无人不确定。

\(i\) 从小到大遍历,对于第 \(i\) 个点若存在人,且下一个点无人,则这人将沿边跑到下一个点,否则不动。

对于不确定的点,可能是有人也可能是没人,问有多少种在遍历完最后一个盒子有人的情况。 \(n \le 5000\) (其实 \(1e6\) 大差不差也能过)

题解

这个题就是纯唐。首先我们发现捏,这个东西显然是一个内向基环树。

然后我们不考虑基环树,就考虑普通树,普通树怎么做?

我们发现如果是概率的话,每个点的概率显然就是 \(0\)(无人),\(1\)(有人),\(\frac{1}{2}\)(不确定)。

好像这个可以搞诶……

那么对于一次转移,即从 \(i\) 到其父亲节点,若以 \(k_1\) 表示 \(i\) 点概率, \(k_2\) 表示 \(fa_i\) 的概率。(转移前)

则转移后新概率:

\[\begin{aligned} k_1' &= k_1 \times k_2 \\ k_2' &= (1 - k_2) \times k_1 + k_2 \end{aligned}\]

感觉还是挺显然的……

那只用从小往大依次枚举就好了呀,做完了。

好的,现在来看正解。

那有些长得帅的小伙伴就会问了,这和树有啥区别吗?难道把边重新跑一遍有啥区别吗?

诶,现在有这么一个环:

1 -> 2
 \  / (2 -> 3 , 3 -> 1)
   3

如果 \(1 , 2 , 3\) 全是 \(?\) , 那么对于 \(2\) 来说 \(2\)\(0\)\(1\) 号有可能 \(1/0\) , \(3\) 同理,对于处理 \(1 \to 3\),时,可能钦定 \(1\) 号为 \(1\),然而 \(3\) 号可能是由 \(1\) 号为 \(0\) 转移来的。因此有错误。

那有些长得帅的小伙伴们又会说了,不是那咋做?

我们发现错误的原因是由于第一个点重复了,那我们钦定第一个值是啥,那不就不会重复了吗!

只要我们钦定这个环里第一次处理的边的值并确定之,最后乘上其为被钦定的值的概率,加起来,乘总数,就得到答案了。时间复杂度 \(\mathcal{O(n)}\)

完结撒花!!!

Code

CODE
#include <bits/stdc++.h>
typedef long long ll ; 
using namespace std ; 
const int N = 5010 ; 
const int mod = 1e9 + 7 ; 

int T , n ; 
char s[N] ; int b[N] ; 

struct Node {
	int father[N] ; ll Probabit[N] ; 

	inline void clear(int n) {
		for (int i = 1 ; i <= n ; ++ i) father[i] = i ; 
		for (int i = 1 ; i <= n ; ++ i) {
			if (s[i] == '.') Probabit[i] = 0 ; 
			else if (s[i] == 'C') Probabit[i] = 1 ; 
			else Probabit[i] = (mod + 1) / 2 ; 
		}
	}

	int Find_Father(int x) {
		if (x == father[x]) return x ; 
		return father[x] = Find_Father(father[x]) ; 
	}
	bool Combine(int x , int y) {
		return Find_Father(x) == Find_Father(y) ; 
	}
	bool Merge(int x , int y) {
		x = Find_Father(x) , y = Find_Father(y) ; 
		if (x == y) return true ; 
		return father[y] = x , false ; 
	}
} ; 

struct Probability {
	ll Probabit[N] ; 
	
	inline void clear(int n) {
		for (int i = 1 ; i <= n ; ++ i) {
			if (s[i] == '.') Probabit[i] = 0 ; 
			else if (s[i] == 'C') Probabit[i] = 1 ; 
			else Probabit[i] = (mod + 1) / 2 ; 
		}
	}

	void Build_Bridge(int x , int y) {
		ll fir = Probabit[x] , sec = Probabit[y] ; 
		Probabit[y] = ((1 - sec + mod) * fir + sec) % mod ; 
		Probabit[x] = (fir * sec) % mod ; 
	}

	ll Ask() {
		ll ans = 0 ; 

		for (int i = 1 ; i <= n ; ++ i) {
			if (s[i] == '?') ans = (ans * 2ll) % mod ; 
		}

		return (ans * Probabit[n]) % mod ; 
	}

	Probability operator = (Probability b) {
		for (int i = 1 ; i <= n ; ++ i) {Probabit[i] = b.Probabit[i] ; }
		return b ; 
	}
} ; 

Node alpha , beta ; Probability theta , delta ; 

signed main() {
	freopen("experiment.in" , "r" , stdin) ; 
	freopen("experiment.out" , "w" , stdout) ; 
	ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ; 
	cin >> T ; 

	while (T --) {
		cin >> n >> s + 1 ; 
		for (int i = 1 ; i <= n ; ++ i) cin >> b[i] ; 

		alpha.clear(n) , beta.clear(n) ; int xrlong = 0 ; 

		for (int i = 1 ; i <= n ; ++ i) alpha.Merge(i , b[i]) ; 
		for (int i = n ; i >= 1 ; -- i) {
			if (alpha.Combine(i , n) && beta.Merge(i , b[i])) {
				xrlong = i ; 
			}
		}

		theta.clear(n) ; 

		if (!xrlong) {			
			for (int i = 1 ; i <= n ; ++ i) {
				theta.Build_Bridge(i , b[i]) ; 
			}

			cout << theta.Ask() << '\n' ; 
			continue ; 
		} else {
			ll ans = 0 ; 
			char s1 = s[xrlong] , s2 = s[b[xrlong]] ; ll p1 , p2 ; 

			for (int i = 1 ; i < xrlong ; ++ i) theta.Build_Bridge(i , b[i]) ; 
			p1 = theta.Probabit[xrlong] , p2 = theta.Probabit[b[xrlong]] ; 
			for (int x = 0 ; x <= 1 ; ++ x) {
				for (int y = 0 ; y <= 1 ; ++ y) {
					ll now = 1 ; 
					delta = theta ; delta.Probabit[xrlong] = x ; 
					delta.Probabit[b[xrlong]] = y ; 
					now = ((x == 0 ? 1 + mod - p1 : p1) * (y == 0 ? 1 + mod - p2 : p2)) % mod ; 

					for (int i = xrlong ; i <= n ; ++ i) {
						delta.Build_Bridge(i , b[i]) ; 
					}

					now = (now * delta.Probabit[n]) % mod ; (ans += now) %= mod ; 
				}
			}

			for (int i = 1 ; i <= n ; ++ i) 
				if (s[i] == '?') ans = (ans * 2) % mod ; 
			cout << ans << '\n' ; 
		}
	}
}
posted @ 2024-10-07 20:01  HANGRY_Sol&Cekas  阅读(50)  评论(6编辑  收藏  举报