题解 环
Description
\(1\leq n\leq 5000\)
Solution
首先将 \(a\) 排序 .
考虑依次考虑左边的每个点并决定它们的出边 .
那么记录右边度数为 \(0/1/2\) 的点的数量的话有 \(O(n^3)\) 的 dp .
但我们可以把度数为 \(1\) 的配对点缩成一个 , 表示选择这一对点时钦定选择这个 .
这样过程就变成了 , 选择右边两个点并连一条有向边 , 并把出点删掉 .
但这个做法只有在把右边点全删完时算出了右边点全部在环内的方案 .
那么我们只需在每次加点时枚举加了几个并钦定它们出现在环中即可 .
时间复杂度 \(O(n^2)\)
code
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
int read()
{
int ret=0;bool f=0;char c=getchar();
while(c>'9'||c<'0')f|=(c=='-'),c=getchar();
while(c>='0'&&c<='9')ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
return f?-ret:ret;
}
const int maxn=5e3+5,mod=998244353;
int qpow(int a,int b){int ret=1;for(;b;b>>=1,a=(ll)a*a%mod)if(b&1)ret=(ll)ret*a%mod;return ret;}
int n,a[maxn],fac[maxn],inv[maxn],f[maxn][maxn],ans;
void prework()
{
fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=(ll)fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n-1;i>=0;i--)inv[i]=(ll)inv[i+1]*(i+1)%mod;
}
int C(int n,int m){return (ll)fac[n]*inv[n-m]%mod*inv[m]%mod;}
int main()
{
freopen("ring.in","r",stdin);
freopen("ring.out","w",stdout);
generate_n(a+1,n=read(),read);
sort(a+1,a+n+1);
prework();
f[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=a[i]-a[i-1];j++)
{
// add j vertexs
for(int k=0;k<=n;k++)
{
// now k+j vertexs
// no choose
(f[i][k+j]+=(ll)f[i-1][k]*C(a[i]-a[i-1],j)%mod)%=mod;
// choose
if(k+j>1)(f[i][k+j-1]+=(ll)f[i-1][k]*C(a[i]-a[i-1],j)%mod*(k+j)%mod*(k+j-1)%mod)%=mod;
else if(k+j==1)(ans+=(ll)f[i-1][k]*C(a[i]-a[i-1],j)%mod)%=mod;
}
}
}
for(int i=1;i<=n;i++)(ans+=mod-a[i])%=mod;
ans=(ll)ans*((mod+1)/2)%mod;
printf("%d\n",ans);
return 0;
}