[ARC132E] Paw

发布时间 2023-12-12 22:48:21作者: Hypercube07

最终状态自左至右一定形如 <<<===>>> ,即中间有一段和原序列相等,左边都是左箭头,右边都是右箭头的形式。

证明考虑如果要保留原序列 \([l,r]\) 一段(显然 \([l,r]\) 中不含 .),那么设位于 \(l\) 以左且距 \(l\) 最近的前两个点为 \(i,j\)(满足 \(i>j\)),如果操作方案中 \(i\) 位于 \(j\) 之前,\(i\) 只能向左走,这样 \(j\) 就成了最近的点且有一段合法后缀,反之如果 \(i\)\(j\) 之后,那么 \(j\) 的操作对合法性没有任何影响。所以最终必然只能形成一段左箭头组成的前缀,后缀同理。

于是我们只需要枚举不变的连续段(对于不存在的情况可以通过在串首尾加 . 来解决),这样前后就是两个独立且等价的问题。对于左面,即是求随机操作至结束不触碰右边界的概率,右边的问题与之对称。

注意到概率实际只与 . 的个数相关,不妨设 \(f_i\) 为上述问题在 . 个数为 \(i\) 时的答案,则有 \(f_i=f_{i-1} \times (1-\frac{1}{2i})\)。式子的意义是除去第一次就触碰边界的情况,其余情况都能递归到下一层。而每种情况被递归到的概率是相等的

代码实现
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef double db;
inline ll read() {
	ll x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const ll N=1e5+5,p=998244353;
int n,pre[N],L[N],R[N],cnt;
ll inv[N],f[N];
string s;
void init() {
	inv[0]=inv[1]=f[0]=1;
	for(int i=2;i<=n;i++) inv[i]=(p-p/i)*inv[p%i]%p;
	for(int i=1;i<=n;i++) f[i]=f[i-1]*(p+1-inv[2]*inv[i]%p)%p;
}
int main() {
    n=read(),cin>>s;s+=".";s="."+s;
    init();int lst=-1;
    for(int i=0;i<=n+1;i++) {
    	pre[i]=pre[i-1]+(s[i]=='<'); 
    	if(s[i]=='.') {
    		if(lst!=-1) L[++cnt]=lst,R[cnt]=i;
    		lst=i;
		}
	}
	ll ans=0;
	for(int i=1;i<=cnt;i++) (ans+=1ll*(pre[R[i]]-pre[L[i]]+L[i])*f[i-1]%p*f[cnt-i]%p)%=p;
	cout<<ans<<'\n';
	return 0;
}