[ACNOI2022]零一串

23 篇文章 0 订阅
14 篇文章 0 订阅

题目

题目背景
“真是太奇怪了,” 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,,Sn1} 满足 S i    ( 1 ⩽ i < n ) S_i\;(1\leqslant i<n) Si(1i<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 n2.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,i1,v1)f(i+1,r,v)(ilrl) 。只能做到 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(0in) 在第 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} μi1 更早删除,反之则更晚。必要性是显然的。而这里实际上有两个命题,二者互为否命题;所以其逆命题成立,即该约束也是充分的。

无论是将操作局限于 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 μl1 右侧的 μ 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值