【UR #6】懒癌
有\(n\)个人,每个各有一条狗。至少一条狗有病。
人可以看到其它部分人的狗是否有病,通过有向图的形式给出,但看不出自己的狗是否有病。
然后有若干天:
- 上午:每个人都去看其它狗。
- 下午:如果存在人能推断出自己的狗有病,那么就把狗枪毙,操作结束(如果有多个人就同时做);否则继续下一天。
对于所有的生病情况,求:结束时间的和,被枪毙的狗数量和。
\(n\le 3000\)
神仙题。
表示自己想了半天想到了个和题解无关的复杂的大模拟,当然时间复杂度很劣……
考虑一个人怎么知道自己的狗有病:那么他先假设自己的狗没病,据此推断出别人的行动是否合理。设\(f_S\)表示生病狗集合,此时结束时间。转移:枚举\(i\in S\),从\(i\)的角度看:能看到的狗的状态固定,假设\(i\)自己没病,剩下的狗枚举状态。对于所有的这些后继状态\(T\),计算\(\max_T f_T\)。于是\(f_S=1+\min_S\max_T f_T\)。
建立一个模型来模拟DP过程:建出原来有向图的补图。一个状态相当于给这个图上一些点染黑。转移可以视作:选择一个黑点,将其变白,对于它的所有后继,决定是否将其染黑。终止状态为所有点变白。
首先可证:在这张图上,如果存在黑点能到达环,那么无解。可以构造:钦定对后继是否染黑时,钦定全部染黑。此时显然怎么转移都无法到达终止状态。
于是把所有能到达环的点删掉,剩下就是个DAG。
然后可证:如果\(S\subseteq T\),\(f(S)\le f(T)\)。因为此时的状态转移也是个DAG,所以可以归纳了。
于是每次转移都直接钦定对后继全部染黑。那么转移中取\(\max\)已经解决,只剩下取\(\min\)。
显然可以贪心:按照拓扑序来,如果当前点为黑,那么就选择这个点进行转移。此时,结束时间就是所有黑点能够到达的点的集合大小。算出\(p_i\)表示图中能到达\(i\)的点数,第一个答案为\(\sum_i (2^{p_i}-1)2^{n'-p_i}\)。
被枪毙的狗的数量:即第一步选择转移之后,\(f_T\)为最小值的个数。考虑怎样转移是最优的,那么第一步转移的一定是 不存在能到达它的黑点 的点。于是第二个答案为\(\sum_i 2^{n'-p_i}\)。
用bitset优化,时间\(O(\frac{n^3}{\omega})\)。
using namespace std;
#include <bits/stdc++.h>
#define N 3005
#define mo 998244353
#define ll long long
int n;
ll pw[N];
int e[N][N];
void read(){
scanf("%d",&n);
for (int i=0;i<n;++i){
static char str[N];
scanf("%s",str);
for (int j=0;j<n;++j)
e[i][j]=str[j]-'0'^1;
e[i][i]=0;
}
}
int dfn[N],low[N],tim;
int st[N],tp,ins[N];
bool del[N];
void tarjan(int x){
low[x]=dfn[x]=++tim;
st[++tp]=x,ins[x]=1;
int tmp=tp;
for (int y=0;y<n;++y)
if (e[x][y]){
if (!dfn[y])
tarjan(y),low[x]=min(low[x],low[y]);
else if (ins[y])
low[x]=min(low[x],dfn[y]);
del[x]|=del[y];
}
if (low[x]==dfn[x]){
bool bz=(tp-tmp+1>1);
for (int i=tmp;i<=tp;++i) bz|=del[st[i]],ins[st[i]]=0;
if (bz)
for (int i=tmp;i<=tp;++i) del[st[i]]=1;
tp=tmp-1;
}
}
int m,p[N];
int deg[N];
queue<int> q;
bitset<N> b[N];
void BFS(){
for (int i=0;i<n;++i)
if (!del[i]){
++m;
for (int j=0;j<n;++j)
if (!del[j] && e[i][j])
deg[j]++;
}
for (int i=0;i<n;++i)
if (!del[i]){
b[i][i]=1;
if (!deg[i])
q.push(i);
}
while (!q.empty()){
int x=q.front();
q.pop();
for (int y=0;y<n;++y)
if (!del[y] && e[x][y]){
b[y]|=b[x];
if (!--deg[y])
q.push(y);
}
}
for (int i=0;i<n;++i)
p[i]=b[i].count();
}
int main(){
read();
for (int i=0;i<n;++i)
if (!dfn[i])
tarjan(i);
BFS();
pw[0]=1;
for (int i=1;i<=n;++i)
pw[i]=pw[i-1]*2%mo;
ll ans1=0,ans2=0;
for (int i=0;i<n;++i)
if (!del[i]){
(ans1+=(pw[p[i]]-1)*pw[m-p[i]])%=mo;
(ans2+=pw[m-p[i]])%=mo;
}
printf("%lld %lld\n",ans1,ans2);
return 0;
}