题目描述

定好了难度,雄心勃勃的吉米多出题斯基开始寻找智慧的神犇星球的居民出题。

然而吉米多出题斯基没有料到,神犇星球的居民告诉吉米多出题斯基:“今年神犇星球经济不景气,大家都想宅在家里,哪有心思出来出题呢?”

为了挽救这一局面,吉米多出题斯基决定为神犇星球建一些高速传送通道促进该星球各地区之间交流题目。

神犇星球有 $n$ 座小城。对于任意两座小城 $v, u$($v \neq u$),吉米多出题斯基想在 $v, u$ 之间建立一个传送时间为 $w(v, u)$ 的无向传送通道,其中 $w(v, u)$ 为不超过 $k$ 的非负整数。建成后,神犇星球的居民可从一座小城出发经过一个或若干个传送通道到达另一座小城交流题目,花费的时间为所有经过的传送通道的传送时间之和。

吉米多出题斯基还没有决定每一个传送通道的传送时间取值,只是对于任意两座小城 $v, u$,决定了从 $v$ 出发到达 $u$ 的最短时间要恰好等于 $d(v, u)$。但吉米多出题斯基日理万机,于是他找到了你 —— 风璃殇做不出题耶维奇,请你帮助吉米多出题斯基数一数有多少种不同的满足条件的传送通道建设方案吧!

由于方案数可能很大,你只用输出方案数对 $998244353$($7 \times 17 \times 2^{23}+1$,一个质数)取模后的结果。

输入

第一行两个整数 $n, k$。保证 $n \ge 1, k \ge 0$。

接下来 $n$ 行,每行有 $n$ 个非负整数,第 $i$ 行的 第 $j$ 个数表示 $d(i,j)$ 的值。

输出

输出一行,一个整数,表示方案数对 $998244353$ 取模的结果。如果无解,则方案数为 $0$。

限制与约定

对于所有数据,有 $1\leq n\leq 400, 0\leq k\leq 10^9, 0\leq d(u,v)\leq 10^{9}$

子任务 分值 $n$ $k$ 其他限制
1 30 $1 \leq n \leq 4$ $0\leq k\leq 5$
2 30 $1 \leq n \leq 400$ $0\leq k\leq 10^9$ 对于所有 $u\neq v$,有 $d(u,v)\geq 1$
3 40 $1 \leq n \leq 400$ $0 \leq k \leq 10^9$

分析1

对于第一个子任务,当最大\(n=4\)的时候有\(6\)条边,暴力设定每条边的边权,然后Floyd跑一遍验证一下即可

#include<cstdio>  
#include<iostream>  
#include<algorithm>  
#include<cstdlib>  
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#include<set>
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m,ans,a[410][410],b[410][410],c[10];

bool check()
{
    for (int k=1;k<=n;k++)
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                if (i!=j && i!=k && j!=k)
                    if (b[i][k]+b[k][j]<b[i][j]) b[i][j]=b[i][k]+b[k][j];
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            if (b[i][j]!=a[i][j]) return false;
    return true;
}

void dfs(int x)
{
    if (x==n*(n-1)/2+1)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                if (i==j) b[i][j]=0;else b[i][j]=-1;
        int s=0;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                if (b[i][j]==-1) b[i][j]=c[++s],b[j][i]=c[s];
        if (check()) ans++;
        return ;
    }
    for (int i=0;i<=m;i++)
        c[x]=i,dfs(x+1);
}

int main()
{
    read(n);read(m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            read(a[i][j]);
    ans=0;
    dfs(1);
    print(ans);puts("");
    return 0;
}

分析2

对于第二个子任务,数据的特点是\(d(x,y)\geq 1(x\neq y)\)

那么对于一条边\((x,y)\),他的\(d(x,y)\)有两种方式得到,一种是其本身就是最短边,或者是通过两条最短边合成的,即\(d(x,y)=d(x,k)+d(k,y)(x\neq z且y\neq z)\)

那么,对于每一个\((x,y)\),探索是否存在这样一个\(z\),若存在,这条边的边权范围是\([d(x,y),k]\),否则就是能是\(d(x,y)\)

#include<cstdio>  
#include<iostream>  
#include<algorithm>  
#include<cstdlib>  
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#include<set>
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
int wt,ss[19];
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m,a[410][410];
LL f[410][410];
const LL mo=998244353;

bool check(int x,int y)
{
    for (int i=1;i<=n;i++)
        if (i!=x && i!=y)
            if (a[x][i]+a[i][y]==a[x][y]) return true;
    return false;
}

int main()
{
    read(n);read(m);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            read(a[i][j]);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
        {
            if (check(i,j)) f[i][j]=(LL)(m-a[i][j]+1);
            else f[i][j]=1LL; 
        }
    LL ans=1;
    for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++)
            ans=ans*f[i][j]%mo;
    print(ans),puts("");
    return 0;
}

分析3

结合分析1和分析2可以得到60分...还不错了啊......

满分算法的分析...我就把劼的题解搬过来吧......

考虑边可以等于 $0$ 的情况,这时 $d_{u,v}=0$ 的边肯定在图中连成了若干个团。我们把每一个团给缩起来。

两个团之间的点对距离一定是相等的,考虑团与团之间的边,此时相当于有重边的 $d_{u,v}>0$ 的情况。假设这是一条 $a$ 重边,如果存在 $k$ 有 $d_{u,v}=d_{u,k}+d_{k,v}$,那么所有边只要大于等于 $d_{u,v}$ 就可以了,即有 $(K-d_{u,v}+1)^a$ 种方案。如果不存在,那么至少要有一条边等于 $d_{u,v}$,其他边都要大于它,即有 $(K-d_{u,v}+1)^a-(K-d_{u,v})^a$ 种方案。

考虑团内部的边,每个团肯定由 $0$ 的边连成了一个联通块,这是一个经典的容斥,令 $f_i$ 为 $n$ 个点的距离为 $0$ 的团的方案数,$g_i$ 为 $n$ 个点的图的方案数。则有 $g_n=(K+1)^{\binom{n}{2}}$,$f_n=g_n-\sum_{i=1}^{n-1} f_i \times g_{n-i} \times \binom{n-1}{i-1} \times K^{i(n-i)}$。

把所有部分的答案乘起来就好了。时间复杂度 $O(n^3)$,期望得分 $100$ 分。

当然最重要的是要判对无解的情况,大致有四种,分别是 $d_{i,j} >K$,$d_{i,i} \neq 0$,$d_{i,j} \neq d_{j,i}$,$d_{i,j} > d_{i,k}+d_{k,j}$,大力 check 一下就可以了。