UOJ #279. 【UTR #2】题目交流通道

题目叙述

给定 \(n\) 个点的图和两两点之间的最短路径长度,并且每条边权值在 \([0,k]\) 内,求标边的长度的方案数。

题解

设两个点 \(u,v\) 之间的边权值为 \(w_{u,v}\) ,最短路径长度为 \(d_{u,v}\)
如果 \(d_{u,v}\) 可以被其他一系列最短路径替代,比如 \(d_{u,v}=d_{u,1}+d_{1,2}+\cdots+d_{x,v}\) ,那么 \(d_{u,v}\) 实际上是不重要的。
而那些重要的点对 \(u,v\) ,一定是连接了一条边权为 \(w_{u,v}\) 的边。
但是这些事情在存在边权为 0 的边的时候显然存在问题。
于是考虑将最短路长度为 0 那些点构成的联通块,全都缩起来。两个联通块之间,如果是重要的,那么只要有一条是那个权值,其他边的权值不小于这个就可以了。因此直接 \(k^c-w^c\) 即可。
剩下的问题就变成边权为 0 的点构成的联通块内部的方案数了。这明显是一个 \(n\) 个点联通图计数。
\(g_n\) 表示 \(n\) 个点的图的方案数(\(2^{binom{n}{2}}\)),\(f_n\) 表示 \(n\) 个点的联通图方案数。
那么有:

\[f_n=g_n-\sum_{i=1}^{n-1}f_ig_{n-i} \]

这可以通过枚举第一个点的联通块大小的方式解释。
最后只要把这些东西全乘起来即可。别忘了判断一些无解的情况,比如不满足三角不等式,不满足 \(d_{u,v}=d_{v,u}\) 之类的事情。

总结

  • \(n\) 个点联通图计数这样的经典容斥。“全都联通”转化为“去掉不联通”。

代码

#include <cstdio>
#include <iostream>
#include <vector>
#define macro_expand(x) #x
#define print_macro(x) printf("%s\n",macro_expand(x))
#define FOR(i,l,r) for(int i=(l);i<=(r);++i)
#define ROF(i,r,l) for(int i=(r);i>=(l);--i)
using namespace std;
typedef long long LL;
const int Mod=998244353;
int add(int &x,int y){return ((x+=y)>=Mod)?(x-=Mod):x;}
int dec(int &x,int y){return ((x-=y)<0)?(x+=Mod):x;}
int ad(int x,int y){return ((x+y)>=Mod)?(x+y-Mod):(x+y);}
int dc(int x,int y){return ((x-y)<0)?(x-y+Mod):(x-y);}
int ml(int x,int y){return (LL)x*y%Mod;}
int ksm(int x,int y){
	int ret=1;
	for(;y;y>>=1,x=ml(x,x))if(y&1)ret=ml(ret,x);
	return ret;
}
bool chkmin(int &x,const int &y){return (x>y)?(x=y,1):0;}
const int MN=405;
int N,K,D[MN][MN],fa[MN],d[MN][MN];
int get(int u){return (u==fa[u])?u:(fa[u]=get(fa[u]));}
void merge(int u,int v){
	u=get(u),v=get(v);
	if(u!=v)fa[u]=v;
}
vector<int> node[MN];
int f[MN];
int fac[MN],caf[MN];
void init(){
	fac[0]=1;
	for(int i=1;i<=N;++i)fac[i]=ml(fac[i-1],i);
	caf[N]=ksm(fac[N],Mod-2);
	for(int i=N-1;i>=0;--i)caf[i]=ml(caf[i+1],i+1);
}
int B(int u,int d){
	if(d<0||u<d)return 0;
	return ml(fac[u],ml(caf[d],caf[u-d]));
}
int main(){
	freopen("pipeline.in","r",stdin);
	freopen("pipeline.out","w",stdout);
	scanf("%d%d",&N,&K);
	init();
	FOR(i,1,N)FOR(j,1,N)scanf("%d",&D[i][j]);
	FOR(i,1,N)FOR(j,i+1,N)FOR(k,1,N)if(k!=i&&k!=j&&D[i][k]+D[k][j]<D[i][j]){
		printf("0\n");
		return 0;
	}
	FOR(i,1,N)FOR(j,1,N)if(D[i][j]!=D[j][i]){
		printf("0\n");
		return 0;
	}
	FOR(i,1,N)FOR(j,1,N)if(D[i][i]!=0){
		printf("0\n");
		return 0;
	}
	// 注意特判
	FOR(i,1,N)fa[i]=i;
	FOR(i,1,N)FOR(j,1,N)if(!D[i][j])merge(i,j);
	FOR(i,1,N)node[get(i)].push_back(i);
	FOR(i,1,N)FOR(j,1,N)d[i][j]=1e9+1;
	FOR(i,1,N)if(fa[i]==i)FOR(j,1,N)if(fa[j]==j){
		for(int x:node[i])for(int y:node[j])
			chkmin(d[i][j],D[x][y]);
	}
	int ans=1;
	FOR(i,1,N)if(fa[i]==i)FOR(j,i+1,N)if(fa[j]==j){
		bool can=0;
		FOR(k,1,N)if(fa[k]==k&&k!=i&&k!=j&&D[i][k]+D[k][j]==D[i][j]){can=1;break;}
		#define t (K-D[i][j])
		#define si ((int)node[i].size())
		#define sj ((int)node[j].size())
		if(can)ans=ml(ans,ksm((t+1)%Mod,si*sj));
		else{
			if(K<D[i][j]){
				printf("0\n");
				return 0;
			}
			// 注意特判!
			ans=ml(ans,dc(ksm((t+1)%Mod,si*sj),ksm(t,si*sj)));
		}
		#undef t
		#undef si
		#undef sj
	}
	for(int i=1;i<=N;++i){
		f[i]=ksm(K+1,i*(i-1)/2);
		for(int j=1;j<=i-1;++j){
			#define t (i-j)
			dec(f[i],ml(f[j],ml(ksm(K+1,t*(t-1)/2),ml(B(i-1,j-1),ksm(K,(i-j)*j)))));
			#undef t
			// 注意算式
		}
	}
	for(int i=1;i<=N;++i)if(fa[i]==i)ans=ml(ans,f[node[i].size()]);
	printf("%d\n",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
posted @ 2022-06-30 13:36  YouthRhythm  阅读(70)  评论(0编辑  收藏  举报