首先,连续段要知道结论:
连续段要么不交,要么包含
所以是一棵树!每个位置的father是后面第一个包含它的
树形DP!
设dp[x],x为根的子树,(设管辖的区间长度为len,也即L[x]),用1~len的数填充,满足L的方案数
也就是,每个son内部合法,
给每个son分配标号区间,使得相邻儿子不会再接在一起
不会再接在一起?
所以可以把每个儿子看成单独一个点,就划归成了:1,1,1,1,...len的方案数!
设f[i]表示,长度为i+1的1,1,1,1....i+1的序列的合法方案数。
$dp[x]=(Pi dp[son]) imes f[L[x]]$
归纳一下,可得$ans=Pi f[|son(x)|]$,$|son(x)|$表示x的儿子个数
求f[n]?
考虑变成排列的逆!
(也就是把映射矩阵的横纵坐标意义交换)
这样,n+1成为了天然的分割点。
f[n-1]中,填入1,
要么之前合法。
要么1分开了一些东西
|son(x)|可以直接单调栈
注意,Poly每次clear需要重新resize
#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){ char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);} template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar(' ');} namespace Modulo{ const int mod=998244353; int ad(int x,int y){return (x+y)>=mod?x+y-mod:x+y;} void inc(int &x,int y){x=ad(x,y);} int sub(int x,int y){return ad(x,mod-y);} int mul(int x,int y){return (ll)x*y%mod;} void inc2(int &x,int y){x=mul(x,y);} int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;} template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);} template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);} } using namespace Modulo; namespace Miracle{ const int N=50000+5; const int G=3; const int GI=332748118; int n,T; int L[N]; struct Poly{ vector<int>f; Poly(){f.clear();} il int &operator[](const int &x){assert(x<f.size());return f[x];} il void resize(int n){f.resize(n);} il int size(){return f.size();} il void clear(){f.clear();} il void out(){for(reg i=0;i<(int)f.size();++i) ot(f[i]);putchar(' ');} }f; int rev[8*N]; int init(int n){ int m=0; for(m=1;m<n;m<<=1); for(reg i=0;i<m;++i){ rev[i]=(rev[i>>1]>>1)|((i&1)?m>>1:0); } return m; } void NTT(Poly &f,int c){ int n=f.size(); for(reg i=0;i<n;++i){ if(i<rev[i]) swap(f[i],f[rev[i]]); } for(reg p=2;p<=n;p<<=1){ int gen; if(c==1) gen=qm(G,(mod-1)/p); else gen=qm(GI,(mod-1)/p); for(reg l=0;l<n;l+=p){ int buf=1; for(reg i=l;i<l+p/2;++i){ int tmp=mul(f[i+p/2],buf); f[i+p/2]=ad(f[i],mod-tmp); f[i]=ad(f[i],tmp); buf=mul(buf,gen); } } } if(c==-1){ int iv=qm(n); for(reg i=0;i<n;++i){ f[i]=mul(f[i],iv); } } } il Poly operator *(Poly F,Poly G){ int n=init(F.size()+G.size()-1); F.resize(n);G.resize(n); NTT(F,1);NTT(G,1); for(reg i=0;i<n;++i) F[i]=mul(F[i],G[i]); NTT(F,-1); return F; } void divi(int l,int r){ if(l==0&&r==1){ f[0]=1;f[1]=2;return; } if(l==r){ f[l]=ad(f[l],mod-mul(f[1],f[l-1],l-2)); f[l]=ad(f[l],mul(l-1,f[l-1])); return; } int mid=(l+r)>>1; divi(l,mid); // cout<<" divi ---------------------------"<<l<<" "<<r<<endl; Poly F,G,K; if(l==0){ // goto s; // cout<<"sol1---- "<<endl; F.resize(mid+1); G.resize(mid+1); for(reg i=1;i<=mid;++i){ F[i]=f[i]; G[i]=mul(f[i],i-1); } // F.out(); // G.out(); // cout<<"hahahaha "<<endl; F=F*G; // F.out(); for(reg i=mid+1;i<=r;++i){ f[i]=ad(f[i],F[i]); } // cout<<" over "<<endl; }else{ // cout<<"sol2**** "<<endl; F.resize(mid-l+1); G.resize(r-l+1); for(reg i=0;i<=mid-l;++i){ F[i]=f[i+l]; } for(reg i=1;i<=r-l;++i){ G[i]=mul(f[i],i-1); } // F.out(); // G.out(); // cout<<" mul "<<endl; K=F*G; // cout<<" K "<<endl; // K.out(); for(reg i=mid+1;i<=r;++i){ f[i]=ad(f[i],K[i-l]); } F.clear();G.clear(); F.resize(mid-l+1);G.resize(r-l+1); // cout<<F.size()<<" len "<<mid-l+1<<endl; for(reg i=0;i<=mid-l;++i){ F[i]=mul(f[i+l],i+l-1); } // cout<<" OK ? "<<endl; for(reg i=1;i<=r-l;++i){ G[i]=f[i]; } K=F*G; for(reg i=mid+1;i<=r;++i){ f[i]=ad(f[i],K[i-l]); } } // s:; // cout<<" end "<<f[3]<<endl; divi(mid+1,r); } int sta[N],top; int main(){ rd(T);rd(n); int m=init(n+1); f.resize(m); divi(0,m-1); // f.out(); while(T--){ for(reg i=1;i<=n;++i) rd(L[i]); top=0; if(L[n]!=n) { puts("0");continue; } int ans=1; bool fl=true; for(reg i=1;i<=n;++i){ int son=0; while(top&&sta[top]-L[sta[top]]+1>=i-L[i]+1) ++son,--top; // cout<<" ii "<<i<<" son "<<son<<endl; if(top){ if(sta[top]>=i-L[i]+1) fl=false; } sta[++top]=i; ans=mul(ans,f[son]); } if(!fl) ans=0; printf("%d ",ans); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* */
树形结构!
然后递归为子问题。
递归为子问题,把。。。看成。。。。
本质就是“缩点”找相似结构。或者归纳