非常神奇的连续段 dp!
题目链接。
题意
给定 和长度为 的序列 。
对于两个长为 的排列 ,我们设 。
称 是好的当且仅当对于每一个
的位置,在矩阵 中不存在一个 的正方形,覆盖了
这个位置,且这个正方形是一个单位矩阵或单位矩阵的镜像。
对好的 方案数计数。
。
题解
设排列 满足 ,那么 。
所以 是好的,当且仅当
中 这个数字所在的连续段长度不超过 。
那么我们只需要计算出合法的
数量,再乘上 ,就是答案了。
考虑连续段
dp,我们每一次按照从小到大的顺序,往序列中加入一个递增或递减的连续段。
考虑设加入之前,一共形成了
条链,那么有这么几种方案:
- 新加入的连续段单独成一条链;
- 新加入的连续段和之前的某一条链首尾相接;
- 新加入的连续段,两边各和之前的某一条链首位相接。
当将新加入的段和别的链拼起来时,需要满足这条链的这一段不能和新加入的连续段组成更大的连续段。
那么直接
表示加完前 个数字,当前有
条链,最新一个连续段完全没有影响/在边上/是一个单点且独立成段的方案数。
这样的话,直接枚举上一个连续段大小,做法就是 的了。
还可以注意到,对于一个固定的
来说,合法的
是一个后缀,所以直接前缀和优化转移即可。
时间复杂度 。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| #include<bits/stdc++.h> #define inf 0x3f3f3f3f3f3f3f3fll #define debug(x) cerr<<#x<<"="<<x<<endl using namespace std; using ll=long long; using ld=long double; using pli=pair<ll,int>; using pi=pair<int,int>; template<typename A> using vc=vector<A>; template<typename A,const int N> using aya=array<A,N>; inline int read() { int s=0,w=1;char ch; while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1; while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } inline ll lread() { ll s=0,w=1;char ch; while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1; while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w; } const int mod=998244353; const int inv2=(mod+1)/2; ll dp[5005][5005][3]; ll fp[2][5005][3]; int a[5005]; int n; inline void Add(ll &a,ll b) { a+=b; if(a>=mod) a-=mod; } int main() { n=read()+1; for(int i=2;i<=n;i++) a[i]=read(); int now=0,lst=1; dp[1][0][0]=1,fp[now][0][0]=1; for(int r=2;r<=n;r++) { swap(now,lst); memset(fp[now],0,sizeof(fp[now])); for(int p=0;p<r;p++) for(int z=0;z<3;z++) { if(p) Add(fp[now][p-1][0],fp[lst][p][z]*(p-1)*(p-z)%mod); Add(fp[now][p][1],fp[lst][p][z]*(2*p-z)%mod); Add(fp[now][p+1][2],fp[lst][p][z]); }
int w=0x3f3f3f3f,mi=0x3f3f3f3f; for(int l=r;l;l--) { mi=min(mi,a[l]); if(mi>=r-l+1) w=l; }
for(int p=0;p<r;p++) for(int z=0;z<3;z++) { if(p) Add(fp[now][p-1][0],(dp[r-2][p][z]-dp[w-2][p][z]+mod)*(p-1)*(2*p-z)%mod); Add(fp[now][p][1],(dp[r-2][p][z]-dp[w-2][p][z]+mod)*(2*p-z)%mod); Add(fp[now][p][0],(dp[r-2][p][z]-dp[w-2][p][z]+mod)*2*p%mod); Add(fp[now][p+1][1],(dp[r-2][p][z]-dp[w-2][p][z]+mod)*2%mod); } for(int x=0;x<=r;x++) for(int y=0;y<3;y++) dp[r][x][y]=(dp[r-1][x][y]+fp[now][x][y])%mod; }
ll ans=0; for(int i=0;i<3;i++) Add(ans,fp[now][1][i]); ans=(ans%mod+mod)%mod;
for(int i=1;i<n;i++) ans=ans*i%mod; printf("%lld\n",ans); return 0; }
|