码迷,mamicode.com
首页 > 其他好文 > 详细

CF1466H Finding satisfactory solutions

时间:2021-01-11 11:22:54      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:void   typedef   getchar   dag   方案   tis   记录   force   play   

题目链接

题意简述

\(n\)\(1\)\(n\) 的排列,第 \(i\) 个排列为 \(P_i\)

定义一个排列 \(A\) 是最优的,当且仅当不存在任意一个排列 \(B\) 满足存在一个下标集合 \(S\) 符合以下所有条件:

(1)\(\forall i\in S\)\(B_i\in S\)

(2)\(\forall i\in S\),在 \(P_i\)\(A_i\) 不能出现在 \(B_i\) 的左边。

(3)\(\exists i\in S\),在 \(P_i\)\(B_i\) 出现在 \(A_i\) 的左边。

给定一个排列 \(A\),求有多少种 \(P_{1\dots n}\) 使得排列 \(A\) 是最优的,对 \(10^9+7\) 取模。

\(1\le n\le 40\)

Solution

显然在 \(P_i\) 中只有出现在 \(A_i\) 及之前的数是有用的。

考虑建一张有向图:如果在 \(P_i\)\(j\) 出现的位置严格在 \(A_i\) 出现的位置左边,则连白边 \((i,j)\),然后对于所有的 \(i\)黑边 \((i,A_i)\)

对应到排列 \(A\) 不是最优的条件,相当于可以选出若干个不相交的环(符合了条件 \(1\)),满足这些环的边不全为黑(符合了条件 \(3\))。

到这里可以发现其实没有必要选若干个不相交的环,只要能选一个不全为黑边的环出来,\(A\) 就不是最优的了。

考虑一个强连通分量,不难发现只要分量内有白边,就一定能选出一个不全为黑边的环出来。

于是我们得到 \(A\) 是最优的条件:图的每个强连通分量内都只有黑边。

由于所有的黑边 \((i,A_i)\) 已经给出,并且所有黑边构成了若干个不相交的环,故把这些环当成一个大点,要求的就是把这些大点连成一个 DAG 的方案数。

同时由于 \(P_i\) 是排列,故如果 \(i\) 点出发的白边有 \(d\) 条,则会带上 \(d!(n-d-1)!\) 的乘积贡献(把 \(P_i\)\(A_i\) 之前和之后的数安排顺序)。

考虑暴力状压 DP:\(f_S\) 表示把集合 \(S\) 内的大点连成 DAG 的方案数。

转移考虑枚举入度为 \(0\) 的点集 \(T\),加上点集 \(T\) 连向点集 \(S-T\) 的方案数乘上 \(f_{S-T}\)

这样不能保证 \(S-T\) 内没有入度为 \(0\) 的点,故还要套一个容斥:设上面算出的结果为 \(g_T\),则真正入度为 \(0\) 的点集恰好为 \(T\) 的方案数应为 \(\sum_{T\subseteq U\subseteq S}(-1)^{|U|-|T|}g_U\)

于是:

\[f_S=\sum_{T\ne\emptyset,T\subseteq U\subseteq S}(-1)^{|U|-|T|}g_U=\sum_{U\ne\emptyset,U\subseteq S}(-1)^{|U|-1}g_U \]

这样就可以枚举子集进行暴力 DP 了,复杂度 \(O(2^n)\)

而本题中我们可以把 \(S\) 记录成每种大小的大点分别出现的次数,枚举子集可以改成枚举 \(U\) 中每种大小的大点出现了多少次后乘一个组合数,这样处理之后复杂度可以通过。

Code

#include <bits/stdc++.h>
#define mp std::make_pair

typedef std::pair<int, int> pii;
typedef std::vector<pii> vpii;

template <class T>
inline void read(T &res)
{
	res = 0; bool bo = 0; char c;
	while (((c = getchar()) < ‘0‘ || c > ‘9‘) && c != ‘-‘);
	if (c == ‘-‘) bo = 1; else res = c - 48;
	while ((c = getchar()) >= ‘0‘ && c <= ‘9‘)
		res = (res << 3) + (res << 1) + (c - 48);
	if (bo) res = ~res + 1;
}

const int N = 45, M = 3e5 + 5, EI = 1e9 + 7;

int n, p[N], ToT, son[M][N], Root, f[M], C[N][N], g[N], h[N][N], fac[N];
vpii nums[M];
bool vis[N];

int dfs(int cur, vpii now)
{
	int u; nums[u = ++ToT] = now; vpii nxt = now;
	int lst = now.empty() ? 0 : now[now.size() - 1].first;
	for (int i = lst; i <= n - cur; i++) if (i)
	{
		if (i == lst) nxt[nxt.size() - 1].second++;
		else nxt.push_back(mp(i, 1));
		son[u][i] = dfs(cur + i, nxt);
		nxt = now;
	}
	return u;
}

void dfs_trans(int u, int dep, int v, int in, int out,
int now, vpii &trans)
{
	if (dep == nums[u].size())
	{
		if (u == v) return;
		trans.push_back(mp(1ll * now * h[out][in] % EI, v));
		return;
	}
	int x = nums[u][dep].second, y = nums[u][dep].first;
	for (int i = 0; i <= x; i++)
		dfs_trans(u, dep + 1, v, in, out, 1ll * now * C[x][i] % EI, trans),
			v = son[v][y], in += y, out -= y, now = EI - now;
}

int dp(int u)
{
	if (f[u] != -1) return f[u];
	if (u == Root) return f[u] = 1;
	vpii trans; int sze = 0, cnt = 0; f[u] = 0;
	for (int i = 0; i < nums[u].size(); i++)
		sze += nums[u][i].first * nums[u][i].second,
			cnt += nums[u][i].second;
	dfs_trans(u, 0, Root, 0, sze, cnt & 1 ? 1 : EI - 1, trans);
	for (int i = 0; i < trans.size(); i++)
		f[u] = (1ll * trans[i].first * dp(trans[i].second) + f[u]) % EI;
	return f[u];
}

int main()
{
	read(n); Root = dfs(0, vpii(0));
	for (int i = 1; i <= n; i++) read(p[i]);
	std::vector<int> cir;
	for (int i = 1; i <= n; i++) if (!vis[i])
	{
		int c = 0;
		for (int j = i; !vis[j]; j = p[j]) c++, vis[j] = 1;
		cir.push_back(c);
	}
	int u = 1;
	std::sort(cir.begin(), cir.end());
	for (int i = 0; i < cir.size(); i++) u = son[u][cir[i]];
	for (int i = 0; i <= n; i++) C[i][0] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= i; j++)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % EI;
	memset(f, -1, sizeof(f)); fac[0] = 1;
	for (int i = 1; i <= n; i++) fac[i] = 1ll * fac[i - 1] * i % EI;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j <= i; j++)
			g[i] = (1ll * fac[j] * fac[n - j - 1] % EI * C[i][j] + g[i]) % EI;
		h[0][i] = 1;
		for (int j = 1; j <= n; j++)
			h[j][i] = 1ll * h[j - 1][i] * g[i] % EI;
	}
	return std::cout << dp(u) << std::endl, 0;
}

CF1466H Finding satisfactory solutions

标签:void   typedef   getchar   dag   方案   tis   记录   force   play   

原文地址:https://www.cnblogs.com/xyz32768/p/14253797.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!