E. Garden of the Sun
题目描述
给定一个包含X
和.
的 (n imes m) 的矩阵,你需要把X
改成.
使得所有X
向四周连边之后构成一棵树。
初始时X
两两没有公共点。
(1leq n,mleq 500)
解法
每空两行把.
全部染成X
,这时候没有环,但也不连通。
空出来的这两行挑一个连起来。
最后特判一下最下面的情况,因为初始时X
之间没有公共边所以不会形成环。
F. BFS Trees
题目描述
有 (n) 个点 (m) 条边的有向图,定义一棵生成树扎根在 (u) 当且仅当 (u) 到所有节点的最短路都是树上路径长度,对所有 ((u,v)) 求出有多少生成树同时扎根在 (u,v)
(nleq 400,mleq 600)
解法
不妨考虑一个简单的情况,怎么求生成树扎根在 (u) 的方案数?
可以求出 (u) 的 ( t bfs) 树,这样就满足 (u) 到所有点的最短路就是树上路径长度,那么考虑把这棵树替换成别的样子,显然对于某个点可以修改他连上去的边,设它连到上一级的边数是 (cnt(u)),那么 (prod cnt(i)) 就是答案。
再扩展到本题的情况,是可以用类似的方法的,只是我们把 ( t bfs) 树以 (u,v) 建出来好像都不太合适,考虑 ((u,v)) 最短路径上的点是一定会出现的(如果最短路有多条那么答案是 (0)),那么把 ( t 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);
}
}