loj#575. 「LibreOJ NOI Round #2」不等关系

题目描述
题解

只想到 n 2 n^2 n2 的dp,然后优化不了qwq

考虑容斥,考虑枚举一下有多少个位置的>是不合法的,其他的>的合法情况是未知的,那对答案的贡献就是 ( − 1 ) c n t (-1)^{cnt} (1)cnt

然后我们可以dp,设 f i , j f_{i,j} fi,j 表示前 i i i 段最多有 j j j 个上升序列,于是我们列出dp式子 f i , j = ∑ k = 0 i − 1 f k , j − 1 ( s i − s k n − s k ) f_{i,j}=\sum_{k=0}^{i-1}f_{k,j-1}(_{s_i-s_k}^{n-s_k}) fi,j=k=0i1fk,j1(sisknsk) ,由于容斥系数是 ± 1 ±1 ±1 ,所以dp其实没必要即第二维,即 f i = − ∑ k = 0 i − 1 f k ( s i − s k n − s k ) f_{i}=-\sum_{k=0}^{i-1}f_{k}(_{s_i-s_k}^{n-s_k}) fi=k=0i1fk(sisknsk) ,然后式子拆开发现可以分治 N t t Ntt Ntt ,这里要注意<位置上的dp值设为 0 0 0 即可

效率: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码
#include <bits/stdc++.h>
using namespace std;
const int N=8e5+5,P=998244353;
int n,A[N],B[N],t,p,f[N],c,G[2]={3,(P+1)/3},s,re[N],jc[N],ny[N];
char ch[N];bool a[N];
int X(int x){if (x>=P) x-=P;return x;}
void pre(int l){
	for (t=1,p=0;t<l;t<<=1,p++);
	for (int i=0;i<t;i++)
		re[i]=(re[i>>1]>>1)|((i&1)<<(p-1));
}
int K(int x,int y){
	int A=1;
	for (;y;y>>=1,x=1ll*x*x%P)
		if (y&1) A=1ll*A*x%P;
	return A;
}
void Ntt(int *g,bool o){
	for (int i=0;i<t;i++)
		if (i<re[i]) swap(g[i],g[re[i]]);
	for (int wn,i=1;i<t;i<<=1){
		wn=K(G[o],(P-1)/(i<<1));
		for (int x,y,j=0;j<t;j+=(i<<1))
			for (int w=1,k=0;k<i;k++,w=1ll*w*wn%P)
				x=g[j+k],y=1ll*w*g[i+j+k]%P,
				g[j+k]=X(x+y),g[i+j+k]=X(x-y+P);
	}
	if (o)
		for (int i=0,v=K(t,P-2);i<t;i++)
			g[i]=1ll*v*g[i]%P;
}
void solve(int l,int r){
	if (l==r){
		if (l==0) f[l]=1;
		else if (!a[l]) f[l]=0;
		else f[l]=1ll*f[l]*(P-ny[n-l])%P;
		return;
	}
	int mid=(l+r)>>1;
	solve(l,mid);
	for (int i=l;i<=mid;i++)
		A[i-l]=1ll*f[i]*jc[n-i]%P;
	for (int i=0;i<=r-l+1;i++) B[i]=ny[i];
	pre(r-l+mid-l+3);Ntt(A,0);Ntt(B,0);
	for (int i=0;i<t;i++)
		A[i]=1ll*A[i]*B[i]%P;Ntt(A,1);
	for (int i=mid+1;i<=r;i++)
		f[i]=X(f[i]+A[i-l]);
	for (int i=0;i<t;i++) A[i]=B[i]=0;
	solve(mid+1,r);
}
int main(){
	scanf("%s",ch+1);n=strlen(ch+1)+1;
	for (int i=1;i<n;i++)
		a[i]=(ch[i]=='>'),c+=a[i];
	jc[0]=1;
	for (int i=1;i<=n;i++)
		jc[i]=1ll*i*jc[i-1]%P;
	ny[n]=K(jc[n],P-2);
	for (int i=n;i;i--)
		ny[i-1]=1ll*i*ny[i]%P;
	solve(0,n);
	for (int i=0;i<=n;i++) s=X(s+f[i]);
	if (c&1) s=X(P-s);
	cout<<s<<endl;return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值