CF1466H Finding satisfactory solutions
CF1466H Finding satisfactory solutions
这题厉害了!
先考虑已知 如何求合法的 。由于是排列,就想和置换环扯上关系。考虑将 与 最喜欢的物品连边,形成内向基环森林,直觉告诉我们这个环一定要直接选,事实也就是如此,否则选择 即可满足不合法条件。这样,每次确定一个环并删去,那么就会形成合法的 。
现在变成有 计数 了。把置换环抠出来,令环上点为白边,每个点向比环上点更喜欢的点连黑边,那么合法等价于不存在包含黑边的环。
由于 很小,考虑状压 DP。转移考虑一层层连黑边,每次枚举新加进来的环,容斥一下有
其中 表示由 向 连黑边的方案数,显然可以先算出一个点连向 的方案数然后乘起来。
枚举向 连了多少条边,有
这个可以 预处理。复杂度瓶颈就在于 DP。观察一下状态数,发现好像比较小,实际上最大为 。假设状态数为 ,随便实现一下可以做到 ,很轻松就能通过。
#include <cstdio> namespace IO { #define isdigit(x) (x >= '0' && x <= '9') template<typename T> inline void read(T &x) { x = 0; char ch = getchar(); int f = 0; for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0'; if(f) x = -x; } template<typename T> inline void write(T x) { if(x < 0) putchar('-'), x = -x; if(x > 9) write(x / 10); putchar(x % 10 + '0'); } #undef isdigit } using namespace IO; const int N = 110; const int M = 1500; int n, a[N], vis[N], cnt[N]; int w[N][N], binom[N][N], fac[N]; int f[M], sz[M], m, subs; const int P = 1e9 + 7; inline void add(int &x, int y) {if((x += y) >= P) x -= P;} inline void sub(int &x, int y) {if((x -= y) < 0) x += P;} inline void encode(int *num, int &x) { x = 0; for(int i = 1; i <= n; ++i) x = x * (cnt[i] + 1) + num[i]; } inline void decode(int x, int *num) { for(int i = n; i >= 1; --i) num[i] = x % (cnt[i] + 1), x /= (cnt[i] + 1); } void prework() { encode(cnt, m); fac[0] = 1; for(int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % P; for(int i = 0; i <= n; ++i) binom[i][0] = 1; for(int i = 1; i <= n; ++i) for(int j = 1; j <= n; ++j) add(binom[i][j] = binom[i - 1][j], binom[i - 1][j - 1]); for(int i = 0; i <= n; ++i) { w[i][0] = 1; for(int j = 0; j <= i; ++j) add(w[i][1], 1ll * binom[i][j] * fac[j] % P * fac[n - j - 1] % P); for(int j = 2; j <= n; ++j) w[i][j] = 1ll * w[i][j - 1] * w[i][1] % P; } static int s[N]; for(int i = 1; i <= m; ++i) { decode(i, s); int cnt = 0; for(int j = 1; j <= n; ++j) cnt += s[j] * j; sz[i] = cnt; } } int main() { read(n); for(int i = 1; i <= n; ++i) read(a[i]); for(int i = 1; i <= n; ++i) { if(vis[i]) continue; int x = i, siz = 0; do { vis[x] = 1, ++siz; x = a[x]; }while(x != i); ++cnt[siz]; } prework(); static int s[N], t[N]; f[0] = 1; for(int i = 0; i <= m; ++i) { decode(i, s); for(int j = 1; j <= i; ++j) { decode(j, t); int flag = 0; int mul = 1, sum = 0; for(int k = 1; k <= n; ++k) { if(t[k] > s[k]) flag = 1; mul = mul * binom[s[k]][t[k]] % P; sum += t[k]; } if(flag) continue; if(sum & 1) add(f[i], 1ll * mul * f[i - j] % P * w[sz[i] - sz[j]][sz[j]] % P); else sub(f[i], 1ll * mul * f[i - j] % P * w[sz[i] - sz[j]][sz[j]] % P); } } printf("%d\n",f[m]); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现