【UR #6】懒癌
作者:互联网
完全图的情况
第一天,所有人看一下有没有得懒癌的狗。如果没有,则自己的得了懒癌,开枪。
如果第一天没有人开枪,则至少有两只得懒癌的狗。第二天如果有人只看到一只得懒癌的狗,则自己的狗一定得了懒癌,开枪。
以此类推,如果第 \(k\) 天有人开枪,一定同时开 \(k\) 枪,总共有恰好 \(k\) 条得懒癌的狗。
状压DP
设 \(dp_U\) 表示懒癌狗的集合为 \(U\) 时开枪时间。
对于所有 \(x \in U\) 的人,它会考虑,如果 \(x\) 狗没有得懒癌,设集合 \(V\) 为满足以下条件的集合:
- $ x \notin V$
- 若 \(x\) 可以看出 \(y\) 有没有得懒癌,则 \(y \in V\) 当且仅当 \(y\) 得懒癌
- 若 \(x\) 看不出 \(y\) 有没有得懒癌, \(y\) 可以在 \(V\) 中也可以不在 \(V\) 中
那么他应当在 \(\max\{ dp_V\}\) 天之前听到枪声。否则,他会在 \(\max\{DP_V\} + 1\) 天枪杀自己的狗。
因此 \(DP_U = \min_x\{ \max\{dp_V + 1\}\}\)
需要注意的时,转移可能会成环,环中的状态永远不会开枪。
算多少只狗被杀死只需要求有多少个 \(x\) 满足 \(\max\{DP_V\} + 1 = DP_U\) 即可。
这样的复杂度是 \(O(4^n n)\)
事实上,一个人可以认为自己看不出是不是懒癌的狗得了懒癌。这样转移就只需要枚举一个集合 \(V\),复杂度优化到 \(O(2^nn)\)
正解
考虑建一张新图:如果 \(u\) 不知道 \(v\) 有没有得懒癌,由 \(u\) 指一边向 \(v\)。
在这张新图上考虑上面的那个 dp。计算 \(DP_U\) 的时候,我们将所有 \(x \in U\) 的节点染黑,所有可以转移到 \(U\) 的集合 \(V\) 是将 \(U\) 中一个点染白,然后染黑其出边所得到的黑点集合。
如果 \(U\) 中的点能到达环, \(DP_U = +\infty\)。
所以所有能到达环的节点必须不得懒癌。把这些点删掉,得到一张 \(DAG\)。
我们发现 \(DP_U\) 的值就可以直接计算了。因为那个转移方程就相当于,每次可以选择将一个黑点染白,然后染黑其所有出边,直到所有点都为白色所需的最小步数,这显然是 \(U\) 的后继节点个数。
而死的狗的数目,就是要使染色步数最小,第一次染色可以选择的黑点数目,这就是没有前驱黑点的黑点数目。
只需要用 bitset 算出每个点可以被多少个点到达即可。
代码实现上,拓扑排序可以用点的入度来做,这样就不会算到能到达环的点了。
总复杂度 \(O(\frac{n^3}{\omega})\)
#pragma GCC optimize("2,Ofast,inline")
#include<bits/stdc++.h>
using namespace std;
const int N = 3e3 + 10;
const int mod = 998244353;
inline void upd(int &x, int y) {
(x += y) >= mod ? x -= mod : 0;
}
int n;
int deg[N], pw[N], a[N][N];
int tot, q[N];
char s[N];
bitset<N> dp[N];
int main() {
pw[0] = 1;
for (int i = 1; i < N; ++i) pw[i] = pw[i - 1] * 2 % mod;
cin >> n;
for (int i = 1; i <= n; ++i) {
scanf("%s", s + 1);
for (int j = 1; j <= n; ++j) {
if (i == j) continue;
a[i][j] = (s[j] == '1');
if (!a[i][j]) ++deg[i];
}
}
for (int i = 1; i <= n; ++i) {
if (deg[i] == 0) q[++tot] = i;
}
for (int i = 1; i <= tot; ++i) {
for (int j = 1; j <= n; ++j) {
if (q[i] != j && !a[j][q[i]] && --deg[j] == 0) {
q[++tot] = j;
}
}
}
for (int i = 1; i <= tot; ++i) {
dp[q[i]][q[i]] = 1;
}
for (int i = tot; i >= 1; --i) {
for (int j = 1; j <= tot; ++j) {
if (i != j && !a[q[i]][q[j]]) dp[q[j]] |= dp[q[i]];
}
}
int ans1 = 0, ans2 = 0;
for (int i = 1; i <= tot; ++i) {
int t = dp[q[i]].count();
upd(ans1, 1LL * (pw[t] - 1) * pw[tot - t] % mod);
upd(ans2, pw[tot - t]);
}
printf("%d %d\n", ans1, ans2);
return 0;
}
标签:int,UR,开枪,DP,懒癌,dp,mod 来源: https://www.cnblogs.com/Vexoben/p/11794003.html