[LibreOJ NOI Round #2]不等关系

不等关系

题解

感觉有点麻烦,可能还是我太差了。

首先我们先考虑不管">“的情况,我们得到的就是一串断断续续的的”<"序列。
很容易发现,连在一起的部分就是要求出一个这么长的递增序列。
我们令这些递增序列的长度为 { a 1 , . . . , a k } \{a_{1},...,a_{k} \} { a1,...,ak},我们就是要从这 n n n个数中得到这 k k k个长度为 a a a的递增序列,这个值是比较好算的,就是 n ! ∏ i = 1 k a i ! \frac{n!}{\prod_{i=1}^{k}a_{i}!} i=1kai!n!
相当于先得到一个 n n n的排列,将其分开后再去掉某一段内部不合法的情况,很明显,对于任意一段,其内部合法的情况应该是唯一的。

对于含有">“的情况,我们可以通过容斥求出。
d p i dp_{i} dpi表示长度为 i i i的排列合法的概率, f i f_{i} fi表示长度为 i i i的排列的合法的情况数, c n t i cnt_{i} cnti表示以 i i i为止的前缀中含有”>"的个数。
由于, f i = ∑ j ∈ [ 1 , i ) , s j = ′ > ′ ( − 1 ) c n t i − 1 − c n t j f j C i i − j f_{i}=\sum_{j\in[1,i),s_{j}='>'}(-1)^{cnt_{i-1}-cnt_{j}}f_{j}C_{i}^{i-j} fi=j[1,i),sj=>(1)cnti1cntjfjCiij
d p i = ∑ j ∈ [ 1 , i ) , s j = ′ > ′ ( − 1 ) c n t i − 1 − c n t j d p j ( i − j ) ! dp_{i}=\sum_{j\in[1,i),s_{j}='>'}(-1)^{cnt_{i-1}-cnt_{j}}\frac{dp_{j}}{(i-j)!} dpi=j[1,i),sj=>(1)cnti1cntj(ij)!dpj
明显,如果直接算概率的话会更好算些,答案为 n ! d p n n!dp_{n} n!dpn

但如果直接这样dp的话是 O ( n 2 ) O(n^2) O(n2)的,只有40pts。
但我们很快就发现上面的式子可以通过多项式算出来,但如果顺着跑 N T T NTT NTT的话是会T飞的,不会真有人顺着跑吧
所以我们要分治NTT,总共NTT的次数还是 n n n级别的,但由于两次相乘的多项式长度相近,常数会更小。

时间复杂度 O ( n l o g   n ) O\left(nlog\, n\right) O(nlogn)

源码

#include
using namespace std;
#define MAXN 200005
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int mo=998244353;
const int G=3,invG=332748118;
template<typename _T>
_T Fabs(_T x){
     return x<0?-x:x;}
template<typename _T>
void read(_T &x){
     
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){
     if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){
     x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,fac[MAXN],inv[MAXN],f[MAXN],cnt[MAXN],dp[MAXN];
int lim,rev[MAXN],a1[MAXN],b1[MAXN];
char str[MAXN];
inline int add(int x,int y){
     return x+y<mo?x+y:x+y-mo;}
inline int qkpow(int a,int s){
     int t=1;while(s){
     if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
void init(){
     
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
	for(int i=2;i<=2e5;i++){
     
		fac[i]=1ll*fac[i-1]*i%mo;
		f[i]=1ll*f[mo%i]*(mo-mo/i)%mo;
		inv[i]=1ll*inv[i-1]*f[i]%mo;
	}
}
void gray(int len){
     
	lim=1;int L=0;while(lim<len)lim<<=1,L++;
	for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<L-1);
}
void NTT(int *A,const int typ){
     
	for(int i=0;i<lim;i++)if(i<rev[i])swap(A[i],A[rev[i]]);
	for(int mid=1;mid<lim;mid<<=1){
     
		const int W=qkpow(typ^1?invG:G,(mo^1)/(mid<<1));
		for(int i=0;i<lim;i+=(mid<<1))
			for(int j=i,Wn=1;j<i+mid;j++,Wn=1ll*Wn*W%mo){
     
				const int x=A[j],y=1ll*Wn*A[j+mid]%mo;
				A[j]=add(x,y);A[j+mid]=add(x,mo-y);
			}
	}
	if(typ^-1)return ;const int iv=qkpow(lim,mo-2);
	for(int i=0;i<lim;i++)A[i]=1ll*A[i]*iv%mo;
}
void sakura(int l,int r){
     
	if(l==r){
     if(l&&str[l]=='>'&&(~cnt[l]&1))dp[l]=mo-dp[l];return ;}
	int mid=l+r>>1;sakura(l,mid);gray(r-l+1);
	for(int i=0;i<lim;i++)a1[i]=b1[i]=0;
	for(int i=l;i<=mid;i++)if(str[i]=='>')a1[i-l]=cnt[i]&1?mo-dp[i]:dp[i];
	for(int i=1;i<=r-l;i++)b1[i-1]=inv[i];
	NTT(a1,1);NTT(b1,1);for(int i=0;i<lim;i++)a1[i]=1ll*a1[i]*b1[i]%mo;NTT(a1,-1);
	for(int i=mid+1;i<=r;i++)dp[i]=add(dp[i],a1[i-l-1]);sakura(mid+1,r);
}
signed main(){
     
	init();scanf("%s",str+1);n=strlen(str+1);str[0]=str[++n]='>';
	for(int i=1;i<=n;i++)cnt[i]=cnt[i-1]+(str[i]=='>');
	dp[0]=1;sakura(0,n);printf("%d",1ll*dp[n]*fac[n]%mo);
	return 0;
}

谢谢!!!

你可能感兴趣的:(------二分------,#,多项式)