Problem

Description

你绞尽脑汁也没有解开智商锁给的迷题,只见哐地一下门就开了:“您与锁的主人智商一致。”

于是你们窃取了大量内部资料,最后端掉了 \(IIIS\)。

但是,虽然 \(IIIS\) 被摧毁了,当地居民仍有大量在星期八休息的,而且看不惯在星期日休息的人,在星期日休息的人同样看不惯在星期八休息的人,于是整个社会秩序被打乱得一塌糊涂。

当地共有 \(2^n - 1\) 个村庄,每个村庄住着 \(n\) 户人家,门牌号分别为 \(1, 2, \dots, n\),每户人家家里养着一条狗。恰逢无药可治的懒癌流行,人人自危。每个村庄都有至少一只狗得了懒癌。一个村庄中,门牌号为 \(i\) 的人家的狗要么得懒癌,要么不得懒癌,一共 \(2^n\) 种情况,再去掉都没得懒癌的情况,一共 \(2^n - 1\) 种。这每种情况都会发生在恰好一个村庄中。

这天来了个善良的人来到每个村庄中,告诉所有人一个爆炸性的新闻:“你们村里至少有一只狗得了懒癌!”

每个村庄中每户人家都不知道自己的狗到底是懒癌还是可爱,但是他能一眼看出某些人家的狗有没有得懒癌。由于这个社会里人与人之间的信任已经崩塌,一个人即使看出别人的狗是否得懒癌也不愿告诉他。

可以用一个 \(n\) 个结点的有向图来描述可见性,\(v\) 到 \(u\) 有一条有向边表示门牌号为 \(v\) 的人家能看出门牌号为 \(u\) 的家里的狗是否得了懒癌,没边则表示看不出。每个人都知道这张有向图。

于是一个残酷的逻辑链条开始启动。对于每个村庄:

第一天,早上每户人家的主人会出门看看别人家的狗,如果一个人能推断出自己家的狗得了懒癌,下午6点整,他就会掏出手枪一枪把自己家的狗毙了。

如果有多个人都在同一天推断出了,那么他们会在下午6点整同时开枪。

每个人都听得到这个村庄里的枪声。如果没有听到枪声,这个村里的人第二天会继续早上出门看狗,推断出自己家狗得了懒癌下午就杀狗。如果还没有听到枪声,第三天也会如此,依次类推。(所以如果一个人听到了枪声那么就不会再开枪杀狗)

作为一个想帮助当地居民调节矛盾的你想要向当地居民展示灾难性的后果,请计算出对于所有前 \(233^n\) 天内有过枪声的村庄:

开枪时间之和。如一个村庄在第 \(k\) 天下午响起枪声,则开枪时间为 \(k\)。(多个人同时开枪只算一次)

死亡的狗的总数。

你只用输出对 \(998244353\)(\(7 \times 17 \times 2^{23} + 1\),一个质数)取模后的结果。

输入格式

第一行一个整数 \(n\),含义如前所述。

接下来 \(n\) 行每行 \(n\) 个字符,表示可见性。这 \(n\) 行中的第 \(v\) 行第 \(u\) 个字符为 “1” 表示 \(v\) 能看出 \(u\) 家的狗是否得懒癌,如果字符为 “0” 表示看不出。保证只会出现 “0” 和 “1” 这两种字符,且对于任意一个满足 \(1 \leq v \leq n\) 的 \(v\),第 \(v\) 行第 \(v\) 列为 “0”。

Input Format

第一行一个整数 \(n\),含义如前所述。

接下来 \(n\) 行每行 \(n\) 个字符,表示可见性。这 \(n\) 行中的第 \(v\) 行第 \(u\) 个字符为 “1” 表示 \(v\) 能看出 \(u\) 家的狗是否得懒癌,如果字符为 “0” 表示看不出。保证只会出现 “0” 和 “1” 这两种字符,且对于任意一个满足 \(1 \leq v \leq n\) 的 \(v\),第 \(v\) 行第 \(v\) 列为 “0”。

Output Format

一行输出两个整数分别表示开枪时间和、死亡的狗的总数。

Sample

Input 1

2
01
00

Output 1

5 3

Input 2

2
01
10

Output 2

4 4

Explanation

Explanation for Input 1

门牌号为 1 的人能看见门牌号为 2 的人家里的狗是不是病狗。

共有三个村庄。

1 病了,2 没病。第一天 1 发现 2 没得病,又知道他们中肯定有一个病了,所以第一天下午开枪杀了狗。开枪时间为 \(1\),死了 \(1\) 只狗。

1 没病,2 病了。第一天 1 发现 2 得了病,于是第一天两个人什么也没干。第二天 2 发现前一天 1 没有开枪,所以下午开枪杀了狗。开枪时间为 \(2\),死了 \(1\) 只狗。

1 病了,2 病了。2 还是第二天开枪杀了狗,1 始终没有开枪。开枪时间为 \(2\),死了 \(1\) 只狗。

所以 \(1 + 2 + 2 = 5\),\(1 + 1 + 1 = 3\)。

Range

对于 \(20\%\), \(n \leq 8\)

对于 \(20\%\), \(n \leq 20\)

对于 \(20\%\), \(n \leq 100\)

对于 \(10\%\), \(n \leq 3000\) 每户人家都能看出其他每户人家的狗有没有得懒癌

对于 \(30\%\), \(n \leq 3000\)

Algorithm

强联通分量

Mentality

好神的题 \(stO\) 。

对于一个主人,我们想一下,他在知道这张关系的有向图的情况下,他会怎么考虑?

最暴力的方法:当然是 \(2\) 的幂次枚举所有 他看不到的狗 的得病情况,然后在所有情况里,取可能的开枪时间的最大值。如果到了这个时间还没有人开枪,那就说明自己的狗狗有问题,当天就会把狗 \(bong\) 了。

不过这个太暴力了。

换一种想法,对于一个狗主人,他的思维模式应该是这样子:先假设自己的狗没有生病,然后对于每个看不到的狗,分别假设它们生了病,然后推断看不到的狗的主人又会怎样推断,最后得到最大天数,而其他狗主人也会向这样递归推理。

则我们考虑对于看不到的关系建有向图。

有病的狗为黑点没病的狗为白点,那么每个主人思考的本质就是将自己的点染白,然后将所有后继结点染黑。

这样一来,天数就只与这种染色关系的传递有关。

随后,我们发现,如果这张图的某个点,它能够传到一个强联通分量里,那就永远不会开枪了,因为这样的推理链会陷入一个无限的死循环。

则对于所有能到达强联通分量的点,我们将它们删去,因为它们若为黑点,则永远不会贡献答案。那么最后就会剩下一个漂亮的 \(DAG\) 。

考虑这种情况下的开枪时间。

对于一个黑点 \(x\),将它能直接到达的所有结点称为后继集合,由于能看到后继集合以外的所有点,那么除了后继集合,它都不需要推测其他结点长什么样子。如果它的后继集合中也存在一个黑点 \(y\),那么由于 \(y\) 能够看到 \(x\) ,根据这个题目最原始的形式(也就是没有有向图限制),则 \(y\) 的一切推论必须建立在 \(x\) 不开枪 的基础上。

换句话说,\(y\) 的开枪时间只可能比 \(x\) 晚。

那么,要计算 \(x\) 的开枪时间,无需管后继集合中的黑点。换句话说,我们只需要计算在 \(x\) 的后继集合中没有黑点,同时 \(x\) 为黑点的最大开枪时间即可。

这样我们需要处理的黑点集合必定满足两两之间互不可达,并且能够看见除了自己的后继集合以外的所有点。那对于任意一个点,它一定能推断出其他点当自己为白点情况下的最小开枪时间,如果到时间还没有开枪,那么当天晚上就会把自己的狗杀掉。

不难得出,这些黑点推断出自己是黑点的时间相同。

考虑使用归纳法。

先看只有一个黑点的情况。

对于当前点 \(x\) ,设 \(x\) 为黑点,图中其他全为白点。

对于出度为零的末梢结点,它能看到所有结点且能看到后继集合,由于一定有黑点,开枪天数为 \(1\)。

对于后继结点出度都为零的结点,它会先假设后继结点都为 \(1\) ,由于后继结点互相可见,那么最大开枪时间为结点个数。

则此结点开枪时间为后继结点个数 \(+1\) 。

依次推论归纳下去,对于每个点,后继集合中没有黑点且自己为黑点的开枪时间为后继集合中的结点个数 \(+1\) 。

换句话说,它的答案就是自己能到达的点数。

考虑存在 \(2\) 个黑点的情况。对于其中任意一个黑点而言,它能知道如果自己的后继集合里都为白点,另一个黑点的开枪时间。如果到时间还没有开枪,则说明自己的后继集合中有黑点。于是还要花上自己的时间来推出自己为黑点。

这样的话,答案就是两个黑点能到达的点数之和。

推论归纳下去,对于一个互不可达的黑点集合,答案为集合中的点能够到达的点数之和。

所以我们第一问要求的就是:在所有情况中,所有黑点的 后继集合的并 的数量和。

考虑转换成每个点的贡献。设每个点能够被 \(k\) 个结点到达,则它的贡献为 \((2^k-1)*2^{n-k}\) 。

对于第二问,就是问所有情况中,不能被任何黑点到达的黑点的数量之和。

这种情况下,每个黑点的贡献为 \(2^{n-k}\) 。

对于求 \(DAG\) 中每个点能被多少个点到达,直接 \(bitset\) 优化,得到 \(O(\frac{nm}{32})\) 复杂度。

完毕。

Code

#include <bits/stdc++.h>
using namespace std;
long long read() {
long long x = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) w = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit(ch)) {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return x * w;
}
const int Max_n = 3005, mod = 998244353;
int n, Res, ans1, ans2;
int cntt, dfn[Max_n], low[Max_n], bel[Max_n], num[Max_n];
int cntp, in[Max_n], top[Max_n];
char P[Max_n][Max_n];
bool bk[Max_n], ins[Max_n];
vector<int> r[Max_n];
bitset<Max_n> s[Max_n];
stack<int> stk;
queue<int> q;
void dfs(int x) {
dfn[x] = low[x] = ++cntt;
stk.push(x), ins[x] = 1;
for (auto to : r[x]) {
if (!dfn[to])
dfs(to), low[x] = min(low[x], low[to]);
else if (ins[to])
low[x] = min(low[x], dfn[to]);
}
if (dfn[x] == low[x]) {
bel[x] = x;
while (stk.top() != x) bel[stk.top()] = x, ins[stk.top()] = 0, stk.pop();
ins[stk.top()] = 0, stk.pop();
}
}
void del(int x) {
for (int i = 1; i <= n; i++)
if (x != i && bk[i] && P[i][x] == '0')
bk[i] = 0, del(i);
}
int ksm(int a, int b) {
int res = 1;
for (; b; b >>= 1, a = 1ll * a * a % mod)
if (b & 1) res = 1ll * res * a % mod;
return res;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("76.in", "r", stdin);
freopen("76.out", "w", stdout);
#endif
Res = n = read();
for (int i = 1; i <= n; i++) {
scanf("%s", P[i] + 1), s[i].set(i), bk[i] = 1;
for (int j = 1; j <= n; j++)
if (P[i][j] == '0' && i != j)
r[i].push_back(j);
}
for (int i = 1; i <= n; i++)
if (!dfn[i]) dfs(i);
for (int i = 1; i <= n; i++) num[bel[i]]++;
for (int i = 1; i <= n; i++)
if (num[bel[i]] > 1) bk[i] = 0;
for (int i = 1; i <= n; i++)
if (!bk[i]) del(i);
for (int i = 1; i <= n; i++) Res -= !bk[i];
for (int i = 1; i <= n; i++)
if (bk[i])
for (auto to : r[i]) in[to]++;
for (int i = 1; i <= n; i++)
if (!in[i] && bk[i]) q.push(i);
while (!q.empty()) {
top[++cntp] = q.front();
for (auto to : r[q.front()])
if (bk[to] && !(--in[to])) q.push(to);
q.pop();
}
for (int i = 1; i <= cntp; i++) {
int x = top[i];
for (auto to : r[x])
if (bk[to]) s[to] |= s[x];
}
for (int i = 1; i <= cntp; i++) {
int x = top[i];
int tot = s[x].count();
ans1 = (ans1 + 1ll * (ksm(2, tot) - 1) * ksm(2, Res - tot) % mod) % mod;
ans2 = (ans2 + ksm(2, Res - tot)) % mod;
}
cout << ans1 << " " << ans2 << endl;
}

【UR #6】懒癌的更多相关文章

  1. 【UOJ#76】【UR #6】懒癌(动态规划)

    [UOJ#76][UR #6]懒癌(动态规划) 题面 UOJ 题解 神....神仙题. 先考虑如果是完全图怎么做... 因为是完全图,所以是对称的,所以我们只考虑一个有懒癌的人的心路历程. 如果只有一 ...

  2. 「UR#6」懒癌

    「UR#6」懒癌 妈妈我居然看了六个小时题解,快救救乌干达的可怜儿童吧. 接下来开始膜官方题解: ​ 其实就算有上面两个结论也不是很好想到任意复杂度的做法,关键在于要想到一个人是怎么推断自己的狗是不是 ...

  3. UOJ #76 -【UR #6】懒癌(思维题)

    UOJ 题面传送门 神仙题. orz czx,czxyyds 首先没有懒癌的狗肯定不会被枪毙,证明显然. 接下来考虑怎样计算一种局面的答案,假设 \(dp_S\) 表示对于有且仅有 \(S\) 中的狗 ...

  4. UOJ #76 【UR #6】懒癌

    确实是一道很不错的题啊. 题目链接 题意 感觉也没什么特别简洁的版本,大家直接看题面吧. 题解 我第一次看到这个类似问题的背景是疯狗,因此下面的题解不自觉的代入了...大家明白意思就好. 我们考虑对于 ...

  5. 【UR #7】水题走四方

    题目描述 今天是世界水日,著名的水题资源专家蝈蝈大臣发起了水题走四方活动,向全世界发放成千上万的水题. 蝈蝈大臣是家里蹲大学的教授,当然不愿意出门发水题啦!所以他委托他的助手欧姆来发. 助手欧姆最近做 ...

  6. ur c题练习

    ur的c果然sxbk啊 ur5:“三个莫比乌斯反演掷地有声"——摘自v(c)f(z)k(y)语录,无删改 ur2:有根树分治裸题,复杂度玄学$O(n\sqrt{n})$. 首先,转化为统计k ...

  7. db2 with ur

    这几天查询DB2数据库,老遇到select * from XXX with ur, 好奇ur是什么作用,现在记录一下. DB2中,共有四种隔离级:RS,RR,CS,UR,DB2提供了这4种不同的保护级 ...

  8. 【UOJ#33】【UR#2】树上GCD 有根树点分治 + 容斥原理 + 分块

    #33. [UR #2]树上GCD 有一棵$n$个结点的有根树$T$.结点编号为$1…n$,其中根结点为$1$. 树上每条边的长度为$1$.我们用$d(x,y)$表示结点$x,y$在树上的距离,$LC ...

  9. uoj #118. 【UR #8】赴京赶考 水题

    #118. [UR #8]赴京赶考 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/118 Description ...

随机推荐

  1. 如何使用pandas分析金融数据

    [摘要]pandas是数据分析师分析数据最常用的三方库之一,结合matplotlib,非常强大. 首先我们收集一些数据. 从东方财富客户端导出券商信托板块2018年11月1日的基础行情和财务数据.分别 ...

  2. luogu P2740 [USACO4.2]草地排水Drainage Ditches |网络流

    题目背景 在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水.这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间.因此,农夫约翰修建了一套排水系统来使贝茜的草地免除被大水淹没 ...

  3. 使用Python进行防病毒免杀

    很多渗透工具都提供了权限维持的能力,如Metasploit.Empire和Cobalt Strike,但是都会被防病毒软件检测到这种恶意行为.在探讨一个权限维持技巧的时候,似乎越来越多的人关注的是,这 ...

  4. BZOJ [ZJOI2007]矩阵游戏(二分图匹配)

    1059: [ZJOI2007]矩阵游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 6390  Solved: 3133[Submit][Stat ...

  5. Mac 配置 PlantUML

    PlantUML简介 UML: Unified Modeling Language 统一建模语言,是非专利的第三代建模和规约语言.UML是一种开放的方法,用于说明.可视化.构建和编写一个正在开发的.面 ...

  6. Ceph 块存储

    任何普通的linux主机都可以充当ceph客户机,客户机通过网络与ceph存储集群交互以存储或检索用户数据.Ceph RBD支持已经添加到linux主线内核中,从2.6.34以及以后版本开始. === ...

  7. Python必学之编译器用哪个好?你用错了吧!

    学python要知道怎么用好编译器.当我们编写Python代码时,我们得到的是一个包含Python代码的以.py为扩展名的文本文件.要运行代码,就需要Python解释器去执行.py文件由于整个Pyth ...

  8. Orleans 配置端口的一些坑

    Orleans的配置有点乱的 整理了下 .Configure<EndpointOptions>(options => { //这里的IP决定了是本机 还是内网 还是公网 option ...

  9. unittest自动化测试框架

    目录 框架的概念 Unittest单元测试框架 常用的assert语句 unittest创建测试代码的方式: unittest构建测试套件(测试用例集合): unittest忽略测试用例: 运行测试集 ...

  10. 基于Storm的WordCount

    Storm WordCount 工作过程 Storm 版本: 1.Spout 从外部数据源中读取数据,随机发送一个元组对象出去: 2.SplitBolt 接收 Spout 中输出的元组对象,将元组中的 ...