题解:P12917 [POI 2021/2022 R3] 小矮人派对 2 / Impreza krasnali 2
light_searcher · · 题解
细节题,转移并不难,难点在于状态的设计。
设
容易观察到有如下性质:
- 如果
a_{i-2}=a_i ,则a_i 必然放在i-1 这个位置上;如果a_{i-1}=a_i ,则a_i 必然放在i-1 或者i 。 - 如果
j<i-1 ,则最终a_j 必然不会放在a_i 的后面。
那么就可以考虑 dp,设只考虑前
那么就可以开始转移了,这个要根据
-
$f_{i,0}=f_{i-1,2}$; -
$f_{i,0}=f_{i-1,1}$, $f_{i,1}=f_{i-1,3}$; -
$f_{i,0}=f_{i-1,2}$, $f_{i,2}=f_{i-1,3}+f_{i-1,4}$; -
$f_{i,0}=f_{i-1,0}$, $f_{i,1}=f_{i-1,4}$, $f_{i,2}=f_{i-1,0}+f_{i-1,1}+f_{i-1,2}$, $f_{i,3}=f_{i-1,3}+f_{i-1,4}$, $f_{i,4}=f_{i-1,0}+f_{i-1,1}+f_{i-1,2}$。
最终答案
#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;
}