题目大意
机翻的效果还不错,这里直接把有道的机翻搞过来了。
我们定义一个图的生成树是一个以顶点s为根的BFS树,当且仅当对于每个节点t,图中s和t之间的最短距离等于生成树中s和t之间的最短距离。
给定一个图,我们定义f(x,y)为该图中同时根为x和y的BFS树的生成树的数目。
给定一个有n个顶点和m条边的无向连通图。对所有i j,计算f(i,j),并对998244353取模
题解
我们先对每个点跑一遍最短路,得到数组dis[i][j],以i为根到j的距离,接下来i,j,k三层循环,外层两个循环代表以i为根和以j为根,然后k遍历所有点,如果dis[i][k]+dis[j][k]-1==dis[i][j],那么证明这个点在i到j之间,这些点的数量不能超过dis[j][i]的数量,否则就证明i和j之间有环,这样就不能建一个满足条件的树,接下来如果dis[x][i]+dis[y][i]-1>dis[x][y]我们只用找这个点的父亲满足dis[y][fa]==dis[y][i]-1&&dis[x][fa]==dis[x][i]-1,那么此时这个点就可以挂在这个父亲下面,我们只要找出有几个这样的父亲就好了,就是这个点所有可能存在的地方,所有点可能的情况乘起来就是答案了
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int MAX=405;
const int MOD=998244353;
vector<long long > v[MAX];
queue<long long > q;
long long dis[MAX][MAX];//x和y之间的点的数量
long long print[MAX][MAX];
int main(){
long long n,m,from,to;
scanf("%lld%lld",&n,&m);
while(m--){
scanf("%lld%lld",&from,&to);
v[from].push_back(to);
v[to].push_back(from);
}
for(int i=1;i<=n;i++){
q.push(i);
dis[i][i]=1;
while(!q.empty()){
from=q.front();
q.pop();
for(int j=0;j<v[from].size();j++){
if(!dis[i][v[from][j]]){
q.push(v[from][j]);
dis[i][v[from][j]]=dis[i][from]+1;
}
}
}
}
for(int x=1;x<=n;x++){
for(int y=x;y<=n;y++){
long long ans=1,numdis=0;
for(int i=1;i<=n;i++){
if(dis[y][i]+dis[x][i]-1==dis[x][y]){
numdis++;
}
}
if(numdis>dis[x][y]){
ans=0;
}
for(int i=1;i<=n;i++){
if(dis[y][i]+dis[x][i]-1>dis[x][y]){
numdis=0;
for(int j=0;j<v[i].size();j++){
if(dis[y][v[i][j]]==dis[y][i]-1&&dis[x][v[i][j]]==dis[x][i]-1){
numdis++;
}
}
ans=ans*numdis%MOD;
if(!ans){
break;
}
}
}
print[x][y]=print[y][x]=ans;
}
}
for(int i=1;i<=n;i++){
printf("%lld",print[i][1]);
for(int j=2;j<=n;j++){
printf(" %lld",print[i][j]);
}
printf("\n");
}
}