Gym 102538H Horrible Cycles
我们把点先按照合理的顺序加入,使得每个左部点加入时都恰好与所有右部点相连。
然后设 \(f_{i,j}\) 表示前 \(i\) 个点,一共有 \(j\) 条链的方案数。这里的链指的是一串相连的点(孤立点也算一条链)。
接下来我们对加入的点进行分类讨论:
-
加入的是右部点。此时这个点没有与任何点连边,故如果选则多了一条链,当然也可以不选。转移方程:\(f_{i,j}\leftarrow f_{i-1,j}+f_{i-1,j-1}\)。
-
加入的是左部点。此时这个点与所有右部点都有连边,对于状态 \(f_{i,j}\) 如果选这个点的话,相当于合并两条链,方案数为 \(\binom {j}{2}\times 2\times 2=j\times (j+1)\times 2\)。但是注意到这个东西在孤立点上有问题。我们再分类讨论一下连接的两部分都是什么:
-
两条链:我们认为这样少算了一次。
-
一孤立点一链:我们认为这样正好。
-
两个点:我们认为这样多算了一次。
-
设一共有 \(j+1\) 条链和孤立点,其中有孤立点 \(k\) 个,不难发现总方案数为 \(k\times (k-1)+2\times k\times (j+1-k)+(j+1-k)\times (j-k)=(j+1)\times j\)。
但是这样我们通过手玩一下会发现,最后会多算一次,所以答案还要除以 \(2\)。
最后,我们统计答案是在即将加入一个左部点时进行的,但是这样如果右边只有一个孤立点会被多算,所以还要减掉 \(\sum a_i\)。
AC code:
#include<bits/stdc++.h>
#define int long long
#define N 5005
#define mod 998244353
#define pii pair<int,int>
#define x first
#define y second
using namespace std;
int T=1,n,a[N],f[N];
void solve(int cs){
cin>>n;
int res=0;
for(int i=1;i<=n;i++){
cin>>a[i];
(res+=mod-a[i])%=mod;
}
sort(a+1,a+n+1);
f[0]=1;
for(int i=1;i<=n;i++){
int t=a[i]-a[i-1];
while(t--){
for(int j=n;j;j--){
(f[j]+=f[j-1])%=mod;
}
}
(res+=f[1])%=mod;
for(int j=1;j<=n;j++){
(f[j]+=f[j+1]*(j+1)%mod*j%mod)%=mod;
}
}
if(res&1)res+=mod;
res>>=1;
cout<<res<<'\n';
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cin>>T;
// init();
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}