1. 程式人生 > 其它 >題解 【AT3950 [AGC022E] Median Replace】

題解 【AT3950 [AGC022E] Median Replace】

\(\large\mathcal{Description}\)

定義一個長度為奇數 \(n\)\(01\) 串為美麗的,當且僅當每次將連續 \(3\) 個位替換為它們的中位數 \(\dfrac{n-1}2\) 次後,這個串變成一個字元 \(1\).

現在給定由 \(0, 1, ?\) 三種字元組成的字串 \(S\),求:將每個問號替換為 \(0/1\) 後,得到一個美麗的字串的方案數,答案對 \(10^9+7\) 取模。

\(1 \le n \le 3\times 10^5\).

\(\large\mathcal{Solution}\)

如果我們已經將所有 \(?\) 填好了,如何判斷得到的 \(S\)

是否是美麗的?

由於操作是“將連續 \(3\) 個位替換為它們的中位數”,即:將 \(000\) 變為 \(0\), 將連續的 \(01\)\(10\) 刪掉,我們考慮維護一個棧。

我們從左到右加入每個字元,記當前棧頂為 \(c\),加入的字元為 \(k\).

  • \(c = 0,\ k = 0:\) 若當前棧中有 \(3\)\(0\),可將它們合併為 \(1\)\(0\).
  • \(c = 0,\ k = 1:\) 刪去當前的 \(1\) 以及棧頂的 \(0\).
  • \(c = 1,\ k = 0:\) 正常加入當前 \(0\).
  • \(c = 1,\ k = 1:\) 若棧中 \(1\) 的個數 \(\ge 2\)
    , 可忽略當前新加入的 \(1\)

則維護後的棧必定滿足兩個性質:

  • \(0, 1\) 的個數不超過 \(2\).
  • 必定是若干個 \(1\) 接著若干個 \(0\)

回到原題,我們設計動態規劃狀態為 \(f_{i, j, k}\) 表示做到第 \(i\) 位,當前棧中有 \(j\)\(1\), 有 \(k\)\(0\) 的方案數,按照上面的過程轉移即可。

最後答案即使 \(f_{n, 2, 0} + f_{n, 2, 1} + f_{n, 2, 2} + f_{n, 1, 0}.\)

\(\large\mathcal{Code}\)

#include <bits/stdc++.h>
#define reg register

using namespace std;

typedef long long LL;
const int N = 300010, p = 1000000007;

int n;
char ch[N];
LL f[N][3][3];

int main()
{
	scanf("%s", ch + 1); n = strlen(ch + 1);
	
	f[0][0][0] = 1;
	
	for (reg int i = 0; i < n; ++ i)
	    for (reg int j = 0; j < 3; ++ j)
	        for (reg int k = 0; k < 3; ++ k)
	        {
	        	if (ch[i + 1] != '0')
	        	{
	        		if (k) f[i + 1][j][k - 1] = (f[i + 1][j][k - 1] + f[i][j][k]) % p;
	        		else f[i + 1][min(j + 1, 2)][k] = (f[i + 1][min(j + 1, 2)][k] + f[i][j][k]) % p;
				}
				
				if (ch[i + 1] != '1')
				{
					if (k == 2) f[i + 1][j][1] = (f[i + 1][j][1] + f[i][j][k]) % p;
					else f[i + 1][j][k + 1] = (f[i + 1][j][k + 1] + f[i][j][k]) % p;
				}
			}
	
	printf("%d\n", (f[n][2][0] + f[n][2][1] + f[n][2][2] + f[n][1][0]) % p);
	return 0;
}