【LOJ#575】【LNR#2】不等關系(容斥,動態規划,分治FFT)


【LOJ#575】【LNR#2】不等關系(容斥,動態規划,分治FFT)

題面

LOJ

題解

一個暴力\(dp\),設\(f[i][j]\)表示考慮完了前\(i\)個位置,其中最后一個數在前面所有數中排名是第\(j\)大,那么轉移的時候枚舉一下當前數是第幾大,並且滿足不等式的限制就可以了,然后拿前綴和優化一下就可以做到\(O(n^2)\)
我們把所有連續的<看成一段,這樣子題目就變成了每次要選出一段連續的上升序列,然后相鄰兩個連續段之間必須滿足前一段的末尾要大於后一段的開頭。
顯然這個大於號是不好處理的,如果我們能夠任意就很好做了。
這些>,即段與段之間的分割的位置的大小情況,如果至少有\(i\)個隨意,方案數是\(g[i]\),那么對於答案的貢獻就是\((-1)^ig[i]\)
再考慮一個\(dp\),我們假設分割出來的所有段中,第\(i\)段的長度是\(a[i]\)。設\(f[i][j]\)表示考慮完了前\(i\)段,選出了\(j\)個上升序列的方案數,這樣子就至少有\(i-j\)個位置是非法的。轉移的話枚舉把哪一段作為一段上升序列,那么就是:

\[f[i][j]=\sum_{k=0}^{i-1}f[k][j-1]*{n-s[k]\choose s[i]-s[k]} \]

其中\(s\)\(a\)的前綴和。
不難發現第二維用處不大,因為容斥系數只有\(\pm 1\),所以可以把容斥系數帶進去直接帶進去而不需要額外記錄第二維來輔助計算。
於是轉移就變成了:

\[f[i]=-\sum_{k=0}^{i-1}f[k]{n-s[k]\choose s[i]-s[k]} \]

發現拆開之后可以卷積,然后有一項是\((s[i]-s[k])!\)不太好弄,因為\(s\)足夠小,所以把\(i\)變到\(s[i]\)位置卷,這樣子\(s[i]-s[k]\)就變成了\(i-k\),那么對於非\(s[i]\)的位置,把它強制弄成\(0\)。這樣子拿分治\(FFT\)卷一下就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAX 524288
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int W[MAX],r[MAX];
void NTT(int *P,int opt,int len)
{
	int N,l=0;for(N=1;N<len;N<<=1)++l;
	for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
	for(int i=1;i<N;i<<=1)
	{
		int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
		for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
		for(int j=0,p=i<<1;j<N;j+=p)
			for(int k=0;k<i;++k)
			{
				int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
				P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
			}
	}
	if(opt==-1)
	{
		reverse(&P[1],&P[N]);
		for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
	}
}
int jc[MAX],jv[MAX],inv[MAX];
int n,a[MAX],ans,cnt;char s[MAX];
bool book[MAX];
int A[MAX],B[MAX],f[MAX];
void CDQ(int l,int r)
{
	if(l==r)
	{
		if(l==0)f[l]=1;
		else if(!book[l])f[l]=0;
		else f[l]=1ll*f[l]*(MOD-jv[n-l])%MOD;
		return;
	}
	int mid=(l+r)>>1;
	CDQ(l,mid);
	for(int i=l;i<=mid;++i)A[i-l]=1ll*f[i]*jc[n-i]%MOD;
	for(int i=1;i<=r-l+1;++i)B[i]=jv[i];
	int N;for(N=1;N<=r-l+1+mid-l+1;N<<=1);
	NTT(A,1,N);NTT(B,1,N);
	for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;
	NTT(A,-1,N);
	for(int i=mid+1;i<=r;++i)f[i]=(f[i]+A[i-l])%MOD;
	for(int i=0;i<N;++i)A[i]=B[i]=0;
	CDQ(mid+1,r);
}
int C(int n,int m){if(n<0||m<0||n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int main()
{
	scanf("%s",s+1);n=strlen(s+1)+1;
	for(int i=1;i<n;++i)if(s[i]=='>')book[i]=true,++cnt;
	inv[0]=inv[1]=jc[0]=jv[0]=1;
	for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
	for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
	CDQ(0,n);
	/*
	f[0]=1;
	for(int i=1;i<=n;++i)
		if(book[i])
			for(int j=0;j<i;++j)
				f[i]=(f[i]+MOD-1ll*f[j]*C(n-j,i-j)%MOD)%MOD;
	*/
	for(int i=0;i<=n;++i)ans=(ans+f[i])%MOD;
	if(cnt&1)ans=(MOD-ans)%MOD;
	printf("%d\n",ans);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM