题解 图

传送门

  • 计数时发现什么东西算重了的时候,先别想着跑路,先看看能不能容斥一下
    别管现在已经几层容斥或套了多少层其它算法了,也许再加一层容斥就能做了

考虑 \(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;
}
posted @ 2022-04-12 07:13  Administrator-09  阅读(1)  评论(0编辑  收藏  举报