UOJ279 题目交流通道

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

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

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

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

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

由于方案数可能很大,你只用输出方案数对
998244353998244353(7×17×223+17×17×223+1,一个质数)取模后的结果。 输入

第一行两个整数 n,kn,k。保证 n≥1,k≥0n≥1,k≥0。

接下来 nn 行,每行有 nn 个非负整数,第 ii 行的 第 jj 个数表示 d(i,j)d(i,j) 的值。 输出

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

如果没有为零的 d 的话,只需要对于所有的i,j寻找 x 使得d[i][x]+d[x][j]=d[i][j],如果找到的话 e(i,j) 就有 kd[i][j]+1 种取法,否则只有 1 种取法。
有零的话先用并查集把相互之间为零的缩成一块,块相互之间的情况同上。内部的话,设g[i]表示 i 个点内部的总方案数,f[i]表示 i 个点,相互之间为零的方案数。那么g[i]=(k+1)i(i1)/2。对于 f 求非法情况,枚举其中某一点x所在互相为零的块大小 j f[i]=g[i]n1j=1Cj1i1f[j]g[ij](k+1)j(ij)
还要大力check一下非法情况。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int mod=998244353;
int f[410],g[410],d[410][410],c[410][410],fa[410],size[410],n,k;
int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int pow(int base,int p)
{
    int ret=1;
    for (;p;p>>=1,base=(LL)base*base%mod)
        if (p&1) ret=(LL)ret*base%mod;
    return ret;
}
bool check()
{
    int i,j,x;
    for (i=1;i<=n;i++)
        if (d[i][i])
            return 1;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
            if (d[i][j]!=d[j][i]||d[i][j]>k)
                return 1;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
            for (x=1;x<=n;x++)
                if (d[i][j]>d[i][x]+d[x][j])
                    return 1;
    return 0;
}
int main()
{
    int i,j,ans=1,flag,x;
    scanf("%d%d",&n,&k);
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
            scanf("%d",&d[i][j]);
    if (check())
    {
        printf("0\n");
        return 0;
    }
    c[0][0]=1;
    for (i=1;i<=n;i++)
        for (j=0;j<=i;j++)
            c[i][j]=((j?c[i-1][j-1]:0)+c[i-1][j])%mod;
    f[0]=g[0]=1;
    for (i=1;i<=n;i++)
    {
        g[i]=f[i]=pow(k+1,i*(i-1)/2);
        for (j=1;j<i;j++)
            f[i]=(f[i]-(LL)f[j]*g[i-j]%mod*c[i-1][j-1]%mod*pow(k,j*(i-j))%mod+mod)%mod;
    }
    for (i=1;i<=n;i++) fa[i]=i;
    for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
            if (!d[i][j])
                fa[find(i)]=find(j);
    for (i=1;i<=n;i++)
        size[find(i)]++;
    for (i=1;i<=n;i++)
        if (fa[i]==i)
            ans=(LL)ans*f[size[i]]%mod;
    for (i=1;i<=n;i++) if (fa[i]==i)
        for (j=i+1;j<=n;j++) if (fa[j]==j)
        {
            flag=0;
            for (x=1;x<=n;x++)
                if (fa[x]==x&&i!=x&&j!=x&&d[i][x]+d[x][j]==d[i][j])
                {
                    flag=1;
                    break;
                }
            if (flag) ans=(LL)ans*pow(k-d[i][j]+1,size[i]*size[j])%mod;
            else ans=(LL)ans*(pow(k-d[i][j]+1,size[i]*size[j])-pow(k-d[i][j],size[i]*size[j])+mod)%mod;
        }
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值