题解 图
- 计数时发现什么东西算重了的时候,先别想着跑路,先看看能不能容斥一下!
别管现在已经几层容斥或套了多少层其它算法了,也许再加一层容斥就能做了
考虑 \(p_i=0\) 怎么做
一个 naive 的想法是令 \(f_{i, j, k, l, m}\) 为前 \(i\) 个数,有 \(j\) 个 0 入度点,\(k\) 个 1 入度点等等
复杂度 \(O(n^5)\),一分没有
尝试优化
- 加数和删数常常可以互相转化,而且同一个问题加入元素和删除元素的复杂度往往并不相同
尝试通过加/删转化以优化复杂度
那么对于本题,考虑从一个 2-正则图开始删点
首先注意到一个点的入度和出度是独立的,可以分开考虑
令 \(f_{i, j, k, l}\) 为删到某个时刻,剩下的入度为 1 的点有 \(i\) 个,入度为 2 的点有 \(j\) 个,etc 的方案数
注意到若前三维确定了,那么第四维只有一个状态是有值的
所以这个东西的复杂度其实是 \(O(n^3)\) 的
那么此时原有限制可以先考虑 \(O(2^n)\) 容斥处理
需要改变 \(f\) 的定义,令 \(f_{i, j, k, l}\) 为加点到某个时刻,……
转移与上面是类似的,就是逆着上面的转移
然后再 DP 优化状压过程,令 \(g_{i, j, k}\) 为前 \(i\) 个数,钦定 \(j\) 个入度为 2,钦定 \(k\) 个入度为 1 方案数
发现直接合并的话入度为 1 的可能有重边
所以再大力容斥一下重边数量即可
复杂度 \(O(n^3)\)
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 502
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
int p[N];
const ll mod=998244353;
namespace force{
int in[N], out[N], ans;
void dfs(int u) {
if (u>n) {
for (int i=1; i<=n; ++i) if (in[i]!=2 || out[i]!=2) return ;
++ans;
return ;
}
for (int i=1; i<=n; ++i) if (i!=p[u])
for (int j=i+1; j<=n; ++j) if (i!=j && j!=p[u]) {
++out[i]; ++out[j];
++in[u]; ++in[u];
dfs(u+1);
--out[i]; --out[j];
--in[u]; --in[u];
}
}
void solve() {
dfs(1);
cout<<ans<<endl;
}
}
// namespace task1{
// ll f[N][N][N][N], fac[N], inv[N];
// inline void add(ll& a, ll b) {a=(a+b)%mod;}
// inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
// void solve() {
// fac[0]=fac[1]=1; inv[0]=inv[1]=1;
// for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
// for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
// for (int i=2; i<=n; ++i) inv[i]=inv[i-1]*inv[i]%mod;
// f[0][n][0][n]=1;
// for (int j=n; ~j; --j) {
// for (int i=n; ~i; --i) {
// for (int k=n; ~k; --k) {
// for (int l=n; ~l; --l) if (f[i][j][k][l]) {
// // cout<<i<<' '<<j<<' '<<k<<' '<<l<<endl;
// if (k) {
// if (i) add(f[i-1][j][k-1][l], i*f[i][j][k][l]);
// if (j) add(f[i+1][j-1][k-1][l], j*f[i][j][k][l]);
// }
// else {
// if (i>1) add(f[i-2][j][k][l-1], C(i, 2)*f[i][j][k][l]);
// if (j>1) add(f[i+2][j-2][k][l-1], C(j, 2)*f[i][j][k][l]);
// if (i&&j) add(f[i][j-1][k][l-1], i*j%mod*f[i][j][k][l]);
// }
// }
// }
// }
// }
// cout<<f[0][0][0][0]<<endl;
// }
// }
// namespace task2{
// int f[N][N][N];
// ll fac[N], inv[N];
// inline void add(int& a, ll b) {a=(a+b)%mod;}
// inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
// void solve() {
// // cout<<double(sizeof(f))/1000/1000<<endl;
// fac[0]=fac[1]=1; inv[0]=inv[1]=1;
// for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
// for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
// for (int i=2; i<=n; ++i) inv[i]=inv[i-1]*inv[i]%mod;
// f[0][n][n]=1;
// for (int j=n; ~j; --j) {
// for (int i=n; ~i; --i) {
// for (int l=n; ~l; --l) {
// int k=(i+2*j-2*l);
// if (k<0||k>n) continue;
// // cout<<i<<' '<<j<<' '<<k<<' '<<l<<endl;
// if (k) {
// if (i) add(f[i-1][j][l], 1ll*i*f[i][j][l]);
// if (j) add(f[i+1][j-1][l], 1ll*j*f[i][j][l]);
// }
// else {
// if (i>1) add(f[i-2][j][l-1], C(i, 2)*f[i][j][l]);
// if (j>1) add(f[i+2][j-2][l-1], C(j, 2)*f[i][j][l]);
// if (i&&j) add(f[i][j-1][l-1], 1ll*i*j%mod*f[i][j][l]);
// }
// }
// }
// }
// cout<<f[0][0][0]<<endl;
// }
// }
namespace task3{
int f[N][N][N];
ll fac[N], inv[N];
inline void add(int& a, ll b) {a=(a+b)%mod;}
inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
void solve() {
fac[0]=fac[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<=n; ++i) inv[i]=inv[i-1]*inv[i]%mod;
f[0][0][0]=1;
for (int j=0; j<=n; ++j) {
for (int i=0; i<=n; ++i) {
for (int l=0; l<=n; ++l) {
int k=(i+2*j-2*l);
if (k<0||k>n) continue;
if (k) {
if (i) add(f[i][j][l], 1ll*i*f[i-1][j][l]);
if (j) add(f[i][j][l], 1ll*j*f[i+1][j-1][l]);
}
else {
if (i>1) add(f[i][j][l], C(i, 2)*f[i-2][j][l-1]);
if (j>1) add(f[i][j][l], C(j, 2)*f[i+2][j-2][l-1]);
if (i&&j) add(f[i][j][l], 1ll*i*j%mod*f[i][j-1][l-1]);
}
}
}
}
cout<<f[0][n][n]<<endl;
}
}
namespace task{
ll fac[N], inv[N];
int f[N][N][N], g[2][N][N], in[N], now, ans;
inline void add(int& a, ll b) {a=(a+b)%mod;}
inline ll C(int n, int k) {return n<k?0:fac[n]*inv[k]%mod*inv[n-k]%mod;}
void solve() {
fac[0]=fac[1]=1; inv[0]=inv[1]=1;
for (int i=2; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
for (int i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for (int i=2; i<=n; ++i) inv[i]=inv[i-1]*inv[i]%mod;
for (int i=1; i<=n; ++i) ++in[p[i]];
f[0][0][0]=1;
for (int j=0; j<=n; ++j) {
for (int i=0; i<=n; ++i) {
for (int l=0; l<=n; ++l) {
int k=(i+2*j-2*l);
if (k<0||k>n) continue;
if (k) {
if (i) add(f[i][j][l], 1ll*i*f[i-1][j][l]);
if (j) add(f[i][j][l], 1ll*j*f[i+1][j-1][l]);
}
else {
if (i>1) add(f[i][j][l], C(i, 2)*f[i-2][j][l-1]);
if (j>1) add(f[i][j][l], C(j, 2)*f[i+2][j-2][l-1]);
if (i&&j) add(f[i][j][l], 1ll*i*j%mod*f[i][j-1][l-1]);
}
}
}
}
g[now][0][0]=1;
for (int i=1; i<=n; ++i,now^=1) {
memset(g[now^1], 0, sizeof(g[now^1]));
for (int j=0; j<=i; ++j) {
for (int k=0; k<=i; ++k) {
add(g[now^1][j+1][k], C(in[i], 2)*g[now][j][k]);
add(g[now^1][j][k+1], -1ll*in[i]*g[now][j][k]);
add(g[now^1][j][k], g[now][j][k]);
}
}
}
for (int j=0; j<=n; ++j) {
for (int k=0; k<=n; ++k) if (g[now][j][k]) {
ll t=0;
// cout<<"jk: "<<j<<' '<<k<<endl;
for (int i=0; i<=k; ++i)
t=(t+(i&1?-1:1)*C(k, i)*f[k-i][n-j-k][n-2*j-k])%mod;
// cout<<"t: "<<t<<' '<<g[now][j][k]<<endl;
ans=(ans+t*g[now][j][k])%mod;
}
}
printf("%lld\n", (ans%mod+mod)%mod);
}
}
signed main()
{
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) p[i]=read();
// force::solve();
task::solve();
return 0;
}