CF1886D Monocarp and the Set

发布时间 2023-10-22 23:20:51作者: Simex

Link

此题目可以从两个方向考虑,正着和倒着,倒着考虑比较容易,首先把所有的数放到一块,如果是'<'或者'>',就是去掉最左边或者最右边的数,这样显然只有一种可能,答案不变。
如果是'?',那么显然可以去掉中间的任意一个,所以答案就是\(\times l-2\),那么对于\(s_n-i\)位置的\(?\),他的贡献就是\(n-i-1\)倍,总而言之,如果\(s_j\)\(?\),那么答案就\(\times j-1\)就可以了。
正着考虑呢??首先把通过\(<\)\(>\)加进来的数记作\(X\),而通过\(?\)加进来的数记作\(Y\),显然的一点就是最后,会充满整个序列,并且有贡献的在于\(Y\),因为\(X\)在加入的时候没有选择位置的能力,但是\(Y\)可以插入任何两个\(X\)的中间,那么我们要算的,就是每个\(Y\)有多少种插入位置。显然对于某一串\(Y\),内部的插入顺序是无所谓的,如果他们的长度为\(l\),那么对于答案的贡献就是\(l!\),然后我们再来考察单个\(Y\)插入的过程,设现在一共有\(k\)个空白,第\(i\)个空白的长度为\(L_i\),那么显然,如果这个\(Y\)插到了第\(i\)个空白里,对于答案的贡献就是\(L_i+1\)倍,然后我们求和,\(\sum_{i=1}^{k} (L_i+1)=\sum_{i=1}^kL_i+k=i-1\),会惊奇的发现无论如何,答案的贡献非常简单,\(i-1\)倍。
然后答案为\(0\)的情况只在第一个字符是\(?\)的情况下出现,进行特殊标记一下,配合上乘法逆元就可以了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
#include<ctime>
#include<bitset>
#define int long long
using namespace std;
int mod=998244353;
int inv[300009];
int n,m;
string s;
int invv(int a,int n){
	int ans=1;
	while(n){
		if(n&1){
			ans=(ans*a)%mod;
		}
		a=a*a%mod;
		n>>=1;
	}
	return ans;
}
void up(int &res,int x){
	res=(res*x)%mod;
}
int x;
char c;
signed main(){
	inv[1]=1;
	scanf("%lld",&n);
	for(int i=2;i<=n;++i){
		inv[i]=invv(i,mod-2);
	}
	scanf("%lld",&m);
	//cout<<inv[3]*3%mod;
	cin>>s;
	int f=0;
	int ans=1;
	int l=s.length();
	for(int i=0;i<l;++i){
		if(s[i]=='?'){
			if(i==0) f=1;
			else up(ans,i);
		}
	}
	printf("%lld\n",f?0:ans);
	for(int i=1;i<=m;++i){
		scanf("%lld",&x);
		cin>>c;
		x--;
		if(c=='?'&&(s[x]=='<'||s[x]=='>')){
			if(x==0) f=1;
			else
			up(ans,x);
		}
		if(c!='?'&&s[x]=='?'){
			if(x==0) f=0;
			else up(ans,inv[x]);
		}
		s[x]=c;
		printf("%lld\n",f?0:ans);
	}
	return 0;
}