题目
题目背景
“真是太奇怪了,”
O
n
e
I
n
D
a
r
k
\sf OneInDark
OneInDark 对
O
L
l
Y
E
\sf OL\!lYE
OLlYE 说,“我去问炎翼鸟和
D
d
(
X
y
X
)
\sf Dd(XyX)
Dd(XyX) 谁把我卷飞了,他们都说不是他。”
“这好办。” O L I Y E \sf OL\!IYE OLIYE 说完,便去找他们各谈了一次话,然后回来说:“我告诉他们,如果他们不说是谁,我卷爷就把他们卷飞;现在他们都承认是自己把你卷飞了。”
题目描述
给定
01
01
01 串
S
n
S_n
Sn,其长度为
n
n
n 。求有多少个不同的
01
01
01 串的序列
{
S
1
,
S
2
,
…
,
S
n
−
1
}
\{S_1,S_2,\dots,S_{n-1}\}
{S1,S2,…,Sn−1} 满足
S
i
(
1
⩽
i
<
n
)
S_i\;(1\leqslant i<n)
Si(1⩽i<n) 是
S
i
+
1
S_{i+1}
Si+1 的子序列且
∣
S
i
∣
=
i
|S_i|=i
∣Si∣=i 。
数据范围与约定
n
⩽
2.5
×
1
0
5
n\leqslant 2.5\times 10^5
n⩽2.5×105 。
思路
等价于每次删掉一个字符。为避免重复,规定删掉的字符必须与下一个字符不同(或者是末尾字符)。
只与相邻元素有关,考虑区间 d p \tt dp dp 。记 f ( l , r , v ) ( v ∈ { 0 , 1 } ) f(l,r,v)\;(v\in\{0,1\}) f(l,r,v)(v∈{0,1}) 为,删去 [ l , r ] [l,r] [l,r] 内元素,且最后一个删去的元素不是 v v v 的方案数。有转移 f ( l , r , v ) = ∑ i = l r [ c i ≠ v ] f ( l , i − 1 , v ⊕ 1 ) f ( i + 1 , r , v ) ( r − l i − l ) f(l,r,v)=\sum_{i=l}^{r}[c_i\ne v]f(l,i{-}1,v{\oplus}1)f(i{+}1,r,v){r-l\choose i-l} f(l,r,v)=∑i=lr[ci=v]f(l,i−1,v⊕1)f(i+1,r,v)(i−lr−l) 。只能做到 O ( n 3 ) \mathcal O(n^3) O(n3) 。
好的问题转化胜过万种优化。这也正是我只能永远地膜拜
O
U
Y
E
\sf OUYE
OUYE 的原因。我们换一种约定:删去 0
时,后一个数字必须是 1
,而删去 1
时前一个数需为 0
。假设第
0
0
0 个字符是 0
,第
(
n
+
1
)
(n{+}1)
(n+1) 个字符是 1
,便无特例。此时,操作相当于 将某个
01
01
01 变为
0
/
1
0/1
0/1 。
至此,我们还看不出什么。离谱的要来了:为了监视
01
01
01 的连通情况,我们放一个 辅助元素
μ
i
(
0
⩽
i
⩽
n
)
\mu_i\;(0\leqslant i\leqslant n)
μi(0⩽i⩽n) 在第
i
i
i 个字符和第
(
i
+
1
)
(i{+}1)
(i+1) 个字符之间。对某个
01
01
01 进行操作时,我们将二者之间的辅助元素也删去。不难发现任意时刻原串都是 01
与辅助元素交错的。
结论出现了:若 c i = 0 c_i=0 ci=0,则 μ i \mu_i μi 比 μ i − 1 \mu_{i-1} μi−1 更早删除,反之则更晚。必要性是显然的。而这里实际上有两个命题,二者互为否命题;所以其逆命题成立,即该约束也是充分的。
无论是将操作局限于 01 01 01 之上,还是添加辅助元素 μ i \mu_i μi,我都不可能想到!我本发了宏愿,想要将此题做出;人类的力量太过 弱小,不依靠 D d ( X y X ) \sf Dd(XyX) Dd(XyX) 我永远也做不到!
而
μ
i
\mu_i
μi 的删除顺序唯一确定序列——删除
μ
i
\mu_i
μi 时,究竟是删 0
还是删 1
是可知的:本次操作后,原本 0
左侧的
μ
l
\mu_l
μl 和 1
右侧的
μ
r
\mu_r
μr 会相邻。那么二者的删除顺序就会告诉我们,中间夹着 0
还是 1
,这也就是剩下的元素。
所以此题等同于经典老题不等关系,无非加了几个 ?
罢了。而 ?
相当于没有限制,所以两侧独立;按照 ?
切开,每个子段独立算就行了。时间复杂度
O
(
n
log
2
n
)
\mathcal O(n\log^2 n)
O(nlog2n) 。如果半在线卷积,可以
O
(
n
log
2
n
log
log
n
)
\mathcal O({n\log^2 n\over\log\log n})
O(loglognnlog2n),但那也太毒瘤了 😂
代码
直接把那道题的代码拿过来就行了。
#include <cstdio> // megalomaniac JZM yydJUNK!!!
#include <iostream> // Almighty XJX yyds!!!
#include <algorithm> // decent XYX yydLONELY!!!
#include <cstring> // Casual-Cut DDG yydOLDGOD!!!
#include <cctype> // oracle: ZXY yydBUS!!!
typedef long long llong;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
for(; isdigit(c); c=getchar()) a = a*10+(c^48);
return a*f;
}
const int MOD = 998244353, LOGMOD = 30;
inline int modAdd(int x, const int &y){
return (x += y) >= MOD ? x-MOD : x;
}
inline int qkpow(llong b, int q){
llong a = 1;
for(; q; q>>=1,b=b*b%MOD) if(q&1) a = a*b%MOD;
return int(a);
}
int g[LOGMOD], inv2[LOGMOD];
void prepare(){
int p = MOD-1, x = 0; inv2[0] = 1;
for(inv2[1]=(MOD+1)>>1; !(p&1); p>>=1,++x)
inv2[x+1] = int(llong(inv2[1])*inv2[x]%MOD);
for(g[x]=qkpow(3,p); x; --x)
g[x-1] = int(llong(g[x])*g[x]%MOD);
}
void ntt(int a[], int n){
for(int w=1<<n>>1,x=n; x; w>>=1,--x)
for(int *p=a; p!=a+(1<<n); p+=(w<<1))
for(int i=0,v=1; i!=w; ++i,v=int(llong(g[x])*v%MOD)){
const llong t = llong(p[i]+MOD-p[i+w])*v%MOD;
p[i] = modAdd(p[i],p[i+w]); p[i+w] = int(t);
}
}
void dntt(int a[], int n){
for(int w=1,x=1; x<=n; w<<=1,++x)
for(int *p=a; p!=a+(1<<n); p+=(w<<1))
for(int i=0,v=1; i!=w; ++i,v=int(llong(g[x])*v%MOD)){
const int t = int(llong(p[i+w])*v%MOD);
p[i+w] = modAdd(p[i],MOD-t), p[i] = modAdd(p[i],t);
}
std::reverse(a+1,a+(1<<n));
for(int *i=a; i!=a+(1<<n); ++i)
*i = int(llong(inv2[n])*(*i)%MOD);
}
inline void array_mul(int a[], int b[], const int &&len){
for(int *i=a,*j=b; i!=a+len; ++i,++j)
*i = int(llong(*i)*(*j)%MOD);
}
const int MAXN = 250005;
int inv[MAXN<<2]; ///< inversion of factorial
int dp[MAXN<<1], tmp1[MAXN<<2], tmp2[MAXN<<2];
char str[MAXN];
int solve(const int &n){
memset(dp+1,0,(n+1)<<2); // clear
dp[0] = MOD-1; str[0] = '1'; // dull
for(int i=0,*me=dp; i<=n; ++i,++me){
*me = (str[i] == '1') ? MOD-(*me) : 0;
int len = 1, j = 0; for(; i>>j&1; ++j,len<<=1);
memcpy(tmp1,me-len+1,len<<2), memset(tmp1+len,0,3*len<<2);
memcpy(tmp2,inv,len<<3), memset(tmp2+(len<<1),0,len<<3);
ntt(tmp1,j+2), ntt(tmp2,j+2); // multiply
array_mul(tmp1,tmp2,len<<2), dntt(tmp1,j+2);
for(int *l=me+1,*r=tmp1+len; r!=tmp1+(len<<1);
++l,++r) *l = modAdd(*l,*r); // contribute
}
int ans = dp[n+1], sgn = 0;
rep(i,1,n) if(str[i] == '1') sgn ^= 1;
rep(i,2,n+1) ans = int(llong(ans)*i%MOD);
return sgn && ans ? MOD-ans : ans;
}
char xyx[MAXN];
int main(){
int n = readint();
prepare(); scanf("%s",xyx+1);
inv[1] = 1; rep(i,2,n+1) inv[i] = int(
llong(MOD-MOD/i)*inv[MOD%i]%MOD);
rep(i,2,n+1) inv[i] = int(llong(inv[i-1])*inv[i]%MOD);
int ans = 1; // avoid mistake
for(int i=1,lst=0; i<=n+1; ++i)
if(xyx[i] == '?' || i == n+1){
memcpy(str+1,xyx+lst+1,i-lst-1);
ans = int(llong(ans)*solve(i-lst-1)
%MOD*inv[i-lst]%MOD), lst = i;
}
rep(i,2,n+1) ans = int(llong(ans)*i%MOD);
printf("%d\n",ans);
return 0;
}