本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000 

作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

   题目链接:http://uoj.ac/problem/279 

  算法一:

  $n<=4$时直接上暴力就可以了,爆搜30分;

  算法二:

  对于没有$d(i,j)(i!=j)=0$的情况,考虑如果不存在$d(i,k)+d(k,j)=d(i,j)$的情况,则只能让$i$,$j$这条边$=d(i,j)$;否则,$(i,j)$这条边的权值就必须$>=d(i,j)$。所以对于每条边,如果满足上式,则方案数乘上$(k-d[i][j]+1)$即可。结合算法一,期望得分60分;

  算法三:

  我们考虑$d[i][j]$可以等于$0$的情况,首先我们把所有距离为$0$的点缩成一个团,因为他们之间距离为$0$,对于外界而言他们是完全等价的,不妨看成一个整体;

  当我只考虑团与团之间的时候转化成了算法二的情况,但是计算有所不同,因为团与团之间可能有大量重边,所以我需要分类讨论,如果存在“中转点”,那么方案数就是所有重边的各自权值取值个数的乘积,否则重边中至少有一条需要等于最短路径,其余的随意,这个可以用容斥计算。

  再考虑团内部的情况,我可以这样想,设$f[i]$为$i$个点的集合之间的使得两两最短距离为$0$的方案数,$g[i]$为$i$个点构成的图的所有方案的方案数。那么单独考虑集合中某个点$p$,则容易想到,如果把两两最短距离为$0$的集合称为一个连通块,那么$p$所在的连通块的$size$如果$<i$则说明不合法,即$size<i$都不合法。那么我们得到了一种经典的容斥方法:用所有的构图方案-$p$所在的连通块的$size<i$的情况。我们可以枚举这个$size$,当$size=j$时,显然我们需要选取另外从$i-1$个点中选$j-1$个点($p$已经默认选好了),再考虑$p$所在的连通块的方案数就是$f[j]$,而$p$之外的情况就是$g[i-j]$,最后考虑$p$所在连通块与外界相连的情况,就是需要保证相连的边均为正整数,否则就不满足假设了,即$K^{(n-i)*i}$种方案数,这些都乘起来。以上的所有方案用乘法原理乘起来就可以了。

  推导式:

  ${g[n]=(K+1)^{\frac{n*(n-1)}{2}}}$

  ${f[n]=g[n]-\sum_{i=1}^{n-1} f[i]*g[n-i]* K^{i*(n-i)}*C_{n-1}^{i-1}}$

  

  不懂的可以看看我的算法三代码,有详细注释。

  算法一+算法二:60分:

  

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 411;
const int MOD = 998244353;
int n,k,dis[MAXN][MAXN];
int w[MAXN][MAXN],cnt,f[MAXN][MAXN];
int dui[MAXN*MAXN][2];
LL ans; inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} inline bool check(){
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=w[i][j];
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(i!=k)
for(int j=1;j<=n;j++)
if(i!=j && k!=j)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(f[i][j]!=dis[i][j]) return false;
return true;
} inline void dfs(int x){
if(x==cnt+1) { if(check()) ans++; return ; }
int from=dui[x][0],to=dui[x][1];
for(int i=0;i<=k;i++) { w[from][to]=w[to][from]=i; dfs(x+1); }
} inline void work(){
n=getint(); k=getint();
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=getint();
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) dui[++cnt][0]=i,dui[cnt][1]=j;
if(n<=4) { dfs(1);}
else {
bool flag; ans=1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++) {
//if(i==j) continue;
flag=false;
for(int l=1;l<=n;l++) {
if(l==i || l==j) continue;
if(dis[i][l]+dis[l][j]==dis[i][j]) { flag=true; break; }
}
if(flag)
ans*=(LL)(k-dis[i][j]+1),ans%=MOD;
}
}
printf("%lld",ans);
} int main()
{
work();
return 0;
}

  

 算法三:100分:

  

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 411;
const int MOD = 998244353;
int n,K,dis[MAXN][MAXN],D[MAXN][MAXN];
int belong[MAXN],cnt,size[MAXN];
LL ans,f[MAXN],g[MAXN],C[MAXN][MAXN];
//f[i]表示为内部距离为0的团的内部连边方案数,g[i]表示任意一个大小为n的图的连边方案数
inline void No_solution(){ printf("0"); exit(0); }
inline LL fast_pow(LL x,LL y){ LL r=1; while(y>0) { if(y&1) r*=x,r%=MOD; x*=x; x%=MOD; y>>=1;} return r; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} inline void check_false(){//考虑不合法的情况
//不对称
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(dis[i][j]!=dis[j][i])
No_solution();
//还可以松弛
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(i!=k)
for(int j=1;j<=n;j++)
if(j!=i && j!=k && dis[i][j]>dis[i][k]+dis[k][j]) No_solution(); } inline void work(){
n=getint(); K=getint(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { dis[i][j]=getint(); if(dis[i][j]>K) No_solution(); }
check_false(); ans=1; bool flag; LL now; //将距离为0缩成团
for(int i=1;i<=n;i++) {
if(dis[i][i]) No_solution(); if(belong[i]) continue;
belong[i]=++cnt; size[cnt]++;
for(int j=i+1;j<=n;j++) if(dis[i][j]==0) belong[j]=cnt,size[cnt]++;
} //缩团之后跑算法一
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(belong[i]!=belong[j]) D[belong[i]][belong[j]]=dis[i][j];
for(int i=1;i<=cnt;i++) {
for(int j=i+1;j<=cnt;j++) {
flag=false;//按照算法一计算每条边的贡献
for(int k=1;k<=cnt;k++) {
if(k==i || k==j) continue;
if(D[i][k]+D[k][j]==D[i][j]) flag=true;
}
//两个团之间有size[i]*size[j]条边,分两种情况讨论
//存在中转点的话,则中间的所有边都要大于等于D[i][j]
if(flag) ans*=fast_pow(K-D[i][j]+1,size[i]*size[j]),ans%=MOD;
//否则,中间的边就是一条等于,其余的全都大于等于,用容斥可以算出来至少一条的情况
else ans*=fast_pow(K-D[i][j]+1,size[i]*size[j])-fast_pow(K-D[i][j],size[i]*size[j]),ans%=MOD;
}
}
C[0][0]=1; for(int i=1;i<=400;i++) { C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j],C[i][j]%=MOD; }
//计算团的内部的贡献,可预处理所有情况
for(int i=1;i<=n;i++) g[i]=fast_pow(K+1,i*(i-1)/2);
for(int i=1;i<=n;i++) {
f[i]=g[i]; now=0;
for(int j=1;j<i;j++) {
now=C[i-1][j-1];
now*=fast_pow(K,j*(i-j)); now%=MOD;
now*=f[j]; now%=MOD;
now*=g[i-j]; now%=MOD;
f[i]-=now; f[i]%=MOD;
}
f[i]+=MOD; f[i]%=MOD;
}
for(int i=1;i<=cnt;i++) ans*=f[size[i]],ans%=MOD;
ans+=MOD; ans%=MOD;
printf("%lld",ans);
} int main()
{
work();
return 0;
}

  

UOJ279 【UTR #2】题目交流通道的更多相关文章

  1. 【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升

    [UOJ#278][UTR #2]题目排列顺序 试题描述 “又要出题了.” 宇宙出题中心主任 —— 吉米多出题斯基,坐在办公桌前策划即将到来的 UOI. 这场比赛有 n 道题,吉米多出题斯基需要决定这 ...

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

    题目描述 定好了难度,雄心勃勃的吉米多出题斯基开始寻找智慧的神犇星球的居民出题. 然而吉米多出题斯基没有料到,神犇星球的居民告诉吉米多出题斯基:"今年神犇星球经济不景气,大家都想宅在家里,哪 ...

  3. uoj279题目交流通道(dp)

    题目大意: 神犇星球有 \(n\) 座小城.对于任意两座小城 \(v,u\)\((v≠u)\),吉米多出题斯基想在 \(v,u\) 之间建立一个传送时间为 \(w(v,u)\)的无向传送通道,其中 \ ...

  4. uoj279 题目交流通道

    题目:告诉你每两个点之间的最短路距离.构造每条边边权<=m的无向完全图.求有多少种不同边权的图满足最短路限制?n<=400. 标程: #include<cstdio> #inc ...

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

    http://uoj.ac/problem/279 先判断答案为0的情况,\(d(i,i)\neq 0\),\(d(i,j)\neq d(j,i)\),\(d(i,j)>d(i,k)+d(k,j ...

  6. uoj#279. 【UTR #2】题目交流通道(容斥+数数)

    传送门 先考虑无解的情况,为以下几种:\(dis_{i,j}+dis_{j,k}<dis_{i,k}\),\(dis_{i,i}\neq 0\),\(dis_{i,j}\neq dis_{j,i ...

  7. 【uoj#280】[UTR #2]题目难度提升 对顶堆+STL-set

    题目描述 给出 $n$ 个数 $a_1,a_2,...,a_n$ ,将其排为序列 $\{p_i\}$ ,满足 $\{前\ i\ 个数的中位数\}$ 单调不降.求字典序最大的 $\{p_i\}$ . 其 ...

  8. UOJ Test Round #2

    昨天晚上打的这个比赛,简直一颗赛艇啊-- 感觉发挥的并不好.比赛的时候比较紧张,最后一题还脑残写了个离散化结果爆零了,哎我怎么这么逗逼-- 讲讲比赛经过吧. 比赛之前逗逼地以为是8:00开始,然后淡定 ...

  9. Noip模拟62 2021.9.26

    T1 Set 真就随机化拿了$90$?? 不过还是有依据的,毕竟这道题出解的几率很大,随出答案的概率也极大 所以不妨打一个随机化 1 #include<bits/stdc++.h> 2 # ...

随机推荐

  1. hdu 5371 Hotaru&#39;s problem【manacher】

    题目链接: http://acm.hdu.edu.cn/showproblem.php? pid=5371 题意: 给出一个长度为n的串,要求找出一条最长连续子串.这个子串要满足:1:能够平均分成三段 ...

  2. Python基础之函数与装饰器

    阅读目录 一.为什么要使用函数 二.函数的定义与调用 三.函数返回值 四.函数的参数 五.本章小结 六.装饰器 一.函数流程图: 函数名的命名规则: 1.函数名必须由字母下划线数字组成,不能是关键字和 ...

  3. 常见Linux/Unix开发辅助命令什锦

    很多零碎命令集锦: 1. 怎样通过命令下载ftp文件 read -s -p "Your passwd: " Passwd; wget --user=YourUserName --p ...

  4. 深入浅出MySQL事务处理和锁机制

    1.      事务处理和并发性 1.1.        基础知识和相关概念 1 )全部的表类型都可以使用锁,但是只有 InnoDB 和 BDB 才有内置的事务功能. 2 )使用 begin 开始事务 ...

  5. 【Cocos2dX(2.x)_Lua开发之三】

    [Cocos2dX(2.x)_Lua开发之三]在Lua中使用自定义精灵(Lua脚本与自创建类之间的访问)及Lua基础讲解 本站文章均为李华明Himi原创,转载务必在明显处注明:(作者新浪微博:@李华明 ...

  6. (一)spark算子 分为3大类

    value类型的算子 处理数据类型为value型的算子(也就是这个算子只处理数据类型为value的数据),可以根据rdd的输入分区与输出分区的关系分为以下几个类型 (1)输入分区与输出分区一对一型 m ...

  7. 安装Struts2 类库

    现在,如果一切正常,那么你可以继续设置您的Struts 2框架.以下是简单的步骤,下载并安装在机器上Struts2. 请选择是否要安装Hibernate在Windows或Unix,然后继续进行下一个步 ...

  8. JSP 生命周期 理解JSP底层功能的关键就是去理解它们所遵守的生命周期

    JSP 生命周期 理解JSP底层功能的关键就是去理解它们所遵守的生命周期. JSP生命周期就是从创建到销毁的整个过程,类似于servlet生命周期,区别在于JSP生命周期还包括将JSP文件编译成ser ...

  9. IEnumerable, IEnumerator接口

    IEnumerable接口 // Exposes the enumerator, which supports a simple iteration over a non-generic collec ...

  10. android 各版本的区别

    三.Android 6.x 新增运行时权限概念 Android6.0或以上版本,用户可以完全控制应用权限.当用户安装一个app时,系统默认给app授权部分基础权限,其他敏感权限,需要开发者自己注意,当 ...