1 条题解
-
0
细节题,转移并不难,难点在于状态的设计。
设 中出现过不同的数的数量为 ,原问题等价于把这 个数放在 个位置上,有些位置空着不放,且 和放 的位置距离不超过 的方案数,最后再乘上 就是答案了。
容易观察到有如下性质:
- 如果 ,则 必然放在 这个位置上;如果 ,则 必然放在 或者 。
- 如果 ,则最终 必然不会放在 的后面。
那么就可以考虑 dp,设只考虑前 个的方案数为 ,有如下几种情况:
- 放在位置 上,且 不在 的后面,这种情况的方案数记为 ;
- 放在位置 上,且 放在 上,记为 ;
- 放在 上,记为 ;
- 放在 上,且 放在 上,记为 ;
- 放在 上,且 不放在 上,记为 (拆出 和 是为了能够转移,这个一会儿再说)。
那么就可以开始转移了,这个要根据 的位置和数量分类讨论来转移:
- ,显然 只能要放在 ,转移 ;
- ,且 ,那么 还是只能放在 ,转移 , ;
- 且 ,转移 , ;
- 且 ,转移 , , , , 。
最终答案 ,最开始的时候特判一下无解的情况,那么这题就做完了,时间复杂度 。
#include<bits/stdc++.h> using namespace std; const int N=1e5+5,mod=1e9+7; int n,a[N],cnt[N],lst[N],f[N][5],sum,ans; vector<int>vec[N]; void add(int &x,int v){ x+=v; if(x>=mod) x-=mod; } void gg(){ cout<<"0\n"; exit(0); } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; if(!cnt[a[i]]) sum++; cnt[a[i]]++; vec[a[i]].push_back(i); } for(int i=1;i<=n;i++){ if(cnt[i]>3) gg(); if(cnt[i]==3&&(vec[i][2]-vec[i][1]>1||vec[i][1]-vec[i][0]>1)) gg(); if(cnt[i]==2&&vec[i][1]-vec[i][0]>2) gg(); } f[1][2]=f[1][4]=1; for(int i=2;i<=n;i++) if(i>=3&&a[i-2]==a[i-1]&&a[i-1]==a[i]) f[i][0]=f[i-1][2]; else if(i>=3&&a[i-2]==a[i]){ f[i][0]=f[i-1][1]; f[i][1]=f[i-1][3]; } else if(a[i-1]==a[i]){ f[i][0]=f[i-1][2]; f[i][2]=f[i-1][3];add(f[i][2],f[i-1][4]); } else{ f[i][0]=f[i-1][0]; f[i][1]=f[i-1][4]; f[i][2]=f[i-1][1];add(f[i][2],f[i-1][0]);add(f[i][2],f[i-1][2]); f[i][3]=f[i-1][3];add(f[i][3],f[i-1][4]); f[i][4]=f[i-1][0];add(f[i][4],f[i-1][1]);add(f[i][4],f[i-1][2]); } ans=f[n][0];add(ans,f[n][1]);add(ans,f[n][2]); for(int i=1;i<=n-sum;i++) ans=1ll*ans*i%mod; cout<<ans<<'\n'; return 0; }
- 1
信息
- ID
- 3429
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者