CF1466H Finding satisfactory solutions

这题厉害了!

先考虑已知 \(b\) 如何求合法的 \(a\)。由于是排列,就想和置换环扯上关系。考虑将 \(i\) 与 \(i\) 最喜欢的物品连边,形成内向基环森林,直觉告诉我们这个环一定要直接选,事实也就是如此,否则选择 \(S = circle\) 即可满足不合法条件。这样,每次确定一个环并删去,那么就会形成合法的 \(a\)。

现在变成有 \(a\) 计数 \(b\) 了。把置换环抠出来,令环上点为白边,每个点向比环上点更喜欢的点连黑边,那么合法等价于不存在包含黑边的环。

由于 \(n\) 很小,考虑状压 DP。转移考虑一层层连黑边,每次枚举新加进来的环,容斥一下有

\[f_S = \sum_{T} (-1)^{|T| + 1} f_{S - T} w_{S - T, T}
\]

其中 \(w_{S,T}\) 表示由 \(T\) 向 \(S\) 连黑边的方案数,显然可以先算出一个点连向 \(S\) 的方案数然后乘起来。

枚举向 \(S\) 连了多少条边,有

\[w_{S, x} = \sum_{i=1}^{|S|} \binom{|S|}{i} i! (n-i-1)!
\]

这个可以 \(O(n^2)\) 预处理。复杂度瓶颈就在于 DP。观察一下状态数,发现好像比较小,实际上最大为 \(1440\)。假设状态数为 \(S\),随便实现一下可以做到 \(O(nS^2)\),很轻松就能通过。

#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;
}

CF1466H Finding satisfactory solutions的更多相关文章

  1. 多校联训 DP 专题

    [UR #20]跳蚤电话 将加边变为加点,方案数为 \((n-1)!\) 除以一个数,\(dp\) 每种方案要除的数之和即可. 点击查看代码 #include<bits/stdc++.h> ...

  2. sentence patterns

    第四部分     推理题 1.世界上每个角落的每个人都有立场,都有背景,都有推理性,能推理出一个人语言的真意,才成就了真正的推理能力: 2.换言之,如果你能通过一个人的说话推理出其身份职业,你的推理能 ...

  3. Images as x-axis labels

    Open-source software is awesome. If I found that a piece of closed-source software was missing a fea ...

  4. Complexity and Tractability (3.44) - The Traveling Salesman Problem

    Copied From:http://csfieldguide.org.nz/en/curriculum-guides/ncea/level-3/complexity-tractability-TSP ...

  5. 理解Backtracking

    Backtracking is an algorithm for finding all solutions by exploring all potential candidates. If the ...

  6. Cplex: MIP Control Callback

    *本文主要记录和分享学习到的知识,算不上原创 *参考文献见链接 之前,我们有简单提到Cplex中的MIP Callback Interface,包括了Informational callback, q ...

  7. [C1] Andrew Ng - AI For Everyone

    About this Course AI is not only for engineers. If you want your organization to become better at us ...

  8. LOJ Finding LCM(math)

    1215 - Finding LCM Time Limit: 2 second(s) Memory Limit: 32 MB LCM is an abbreviation used for Least ...

  9. Finding LCM (最小公倍数)

    Finding LCM Time Limit: 2000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu [Submit]   ...

  10. 14 Finding a Shared Motif

    Problem A common substring of a collection of strings is a substring of every member of the collecti ...

随机推荐

  1. 【深入浅出 Yarn 架构与实现】2-2 Yarn 基础库 - 底层通信库 RPC

    RPC(Remote Procedure Call) 是 Hadoop 服务通信的关键库,支撑上层分布式环境下复杂的进程间(Inter-Process Communication, IPC)通信逻辑, ...

  2. Re:从零开始教你使用 Sublime Text

    目录 Re:从零开始教你使用 Sublime Text 0.前言 0-0.关于我为什么要写这篇文章 0-1.关于这篇文章会讲什么 0-2.适用人群 0-4.其他 0-5.无用的统计 1.安装和基础功能 ...

  3. 第一章 计算机和C++简介

    1.1 简介 C++是一种强大的计算机面向对象编程的程序设计语言,它是制造软件的一种编程语言,适合程序员和刚接触编程的技术人员.当今智能手机销量爆炸式增长给移动应用程序的开发带来了很多机会,而C++就 ...

  4. 使用CRD扩展Kubernetes API

    本文是如何创建 CRD 来扩展 Kubernetes API 的教程.CRD 是用来扩展 Kubernetes 最常用的方式,在 Service Mesh 和 Operator 中也被大量使用.因此读 ...

  5. SPFA和链式前向星

    链式前向星 一种存储图的数据结构 建立一个结构体和一个数组加一个变量,这里的to代表边\((u,v)\)中的\(v\)结点,而\(edge\)数组的索引\(idx\)代表\(u\),其中\(w\)代表 ...

  6. Day21:方法重写以及注意细节

    目录 方法重写 什么是方法重写? 方法重写有什么用? 方法重写的注意细节 方法重写 什么是方法重写? 方法重写指的是当子类和父类出现了一摸一样的方法声明 方法重写有什么用? 当父类中有一个方法时,子类 ...

  7. gcc和g++,c和cpp

    gcc对.c文件当成c处理,把.cpp文件当成c++处理 g++对.c和.cpp都当成c++处理 小心会遇到错误

  8. python-opencv实现抖动算法

    抖动算法简单介绍 简单说就是牺牲分辨率来提高颜色数量. 通过黑点的疏密程度来进行灰度的显示. 例如墨水屏幕只能显示黑白,那么我们可以取样一部分区域矩,例如2x2的一个矩阵,来显示5个级别的灰度,用4个 ...

  9. 解决mysql本地连接速度慢

    解决方法 用127.0.0.1而不用localhost 原因 听说是有什么DNS的反向解析

  10. 一文了解 Dubbo 的代码架构

    整体设计 图例说明: 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口. 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层 ...