题目链接->->->min
分析:
min(api,api+1)≤min(api+1,api+2)
一开始博主根据上述条件胡了好几个假结论.....
上述条件可以转化为条件T:把a中数加入到一个空数组中,从前往后,如果当前加入的数是当前所剩数中最小的数,那么加入的下一个数可以任意,否则加入的下一个数必须是所剩数中的最小的数,或者换句话来说,不能够连续加入的两个数都不是当前剩余的最小数。这样做我们可以得到一个数组,注意其中的数是带标号的。
那么这样可以很容易想到dp,但是由于有后效性被ka掉了。
逆向考虑:从小到大加入每一个数。假设当前要加入数x,那么对于当前的数列来说,x只能被加在数列末尾,或者满足条件的中间位置。这个满足条件是什么?就是对于当前数列中的相邻两个数,如果他们都满足根据我上述所说的条件T中的都是当前剩余的数中的最小值(依据条件T中的构造方式),那么数x就可以被插入到这两个数之间。
那么我们可以用dp维护当前有i个可插入位置的方案数。
博主的思维非常诡异,所以选取了上述内容作为博主自己对这道题的看法,而且其中的分析过程有点......
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e3+10;
const ll mod=998244353;
int fac[N],inv[N],a[N],b[N],f[2][N];
int T,n,x;
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int mul(int x,int y){return (ll)x*y%mod;}
void init(){
fac[0]=1;
for (int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
inv[0]=inv[1]=1;
for (int i=2;i<N;i++) inv[i]=mul((mod-mod/i),inv[mod%i]);
for (int i=2;i<N;i++) inv[i]=mul(inv[i-1],inv[i]);
}
int C(int x,int y){
if (x<0||y<0||x<y) return 0;
return mul(mul(fac[x],inv[y]),inv[x-y]);
}
int main(){
scanf("%d",&T);
init();
while (T--){
scanf("%d",&n);
memset(a,0,sizeof(a));
for (int i=1;i<=n;i++){
scanf("%d",&x); a[x]++;
}
int cnt=0;
for (int i=1;i<=n;i++)
if (a[i]>=1) b[++cnt]=a[i];
for (int i=0;i<=n;i++) f[1][i]=f[0][i]=0;
f[1][b[1]]=fac[b[1]];
int s=b[1];
// for (int i=1;i<=n;i++) printf("%d\n",b[i]);
for (int i=2;i<=cnt;i++) {
for (int j=0;j<=s+b[i];j++) f[i&1][j]=0;
for (int j=0;j<=b[i];j++)
for (int k=max(2*j-b[i],0);k<=s;k++)
if (f[i&1^1][k])
f[i&1][k-j+b[i]-j]=add(f[i&1][k+b[i]-2*j],mul(f[i&1^1][k],mul(mul(fac[b[i]-j],fac[j]),mul(C(b[i],j),C(k,j)))));
s+=b[i];
// for (int j=0;j<=s;j++) printf("%d\n",f[i&1][j]);
}
int ans=0;
for (int i=0;i<=n;i++) ans=add(ans,f[cnt&1][i]);
printf("%d\n",ans);
}
}