Codeforces Round #706 (Div. 2)
E. Garden of the Sun
题目描述
给定一个包含X
和.
的 \(n\times m\) 的矩阵,你需要把X
改成.
使得所有X
向四周连边之后构成一棵树。
初始时X
两两没有公共点。
\(1\leq n,m\leq 500\)
解法
每空两行把.
全部染成X
,这时候没有环,但也不连通。
空出来的这两行挑一个连起来。
最后特判一下最下面的情况,因为初始时X
之间没有公共边所以不会形成环。
F. BFS Trees
题目描述
有 \(n\) 个点 \(m\) 条边的有向图,定义一棵生成树扎根在 \(u\) 当且仅当 \(u\) 到所有节点的最短路都是树上路径长度,对所有 \((u,v)\) 求出有多少生成树同时扎根在 \(u,v\)
\(n\leq 400,m\leq 600\)
解法
不妨考虑一个简单的情况,怎么求生成树扎根在 \(u\) 的方案数?
可以求出 \(u\) 的 \(\tt bfs\) 树,这样就满足 \(u\) 到所有点的最短路就是树上路径长度,那么考虑把这棵树替换成别的样子,显然对于某个点可以修改他连上去的边,设它连到上一级的边数是 \(cnt(u)\),那么 \(\prod cnt(i)\) 就是答案。
再扩展到本题的情况,是可以用类似的方法的,只是我们把 \(\tt bfs\) 树以 \(u,v\) 建出来好像都不太合适,考虑 \((u,v)\) 最短路径上的点是一定会出现的(如果最短路有多条那么答案是 \(0\)),那么把 \(\tt bfs\) 树以这个最短路为"根"建出来,对于其他的点选到 \(u,v\) 同时都是最短路的边即可,然后用乘法原理。
时间复杂度 \(O(n^3)\)
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 505;
const int MOD = 998244353;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,f[M][M];vector<int> g[M];
signed main()
{
n=read();m=read();
memset(f,0x3f,sizeof f);
for(int i=1;i<=n;i++) f[i][i]=0;
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
g[u].push_back(v);
g[v].push_back(u);
f[u][v]=f[v][u]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
for(int i=1;i<=n;i++,puts(""))
for(int j=1;j<=n;j++)
{
int ans=1,t=0;
for(int k=1;k<=n;k++)
if(f[i][k]+f[k][j]==f[i][j])
t++;
if(t>f[i][j]+1) ans=0;
for(int k=1;k<=n;k++)
if(f[i][k]+f[k][j]!=f[i][j])
{
int cnt=0;
for(int v:g[k])
cnt+=(f[i][v]+1==f[i][k]
&& f[j][v]+1==f[j][k]);
ans=1ll*ans*cnt%MOD;
}
printf("%d ",ans);
}
}