1. 程式人生 > 其它 >NOIP 模擬 $88\; \rm 最短路徑$

NOIP 模擬 $88\; \rm 最短路徑$

題解

題解 \(by\;zj\varphi\)

意思就是求 \(k\) 點所構成的虛樹遍歷一遍所有點的最短路徑。

最優決策就是所有邊乘 \(2\),再減去當前聯通塊的直徑,則原問題可以轉化為求所有邊和的期望,和所有直徑的期望。

一條邊有貢獻,當且僅當它把原樹分成的兩部分中都有關鍵點。

求直徑的時候直接 \(m^2\) 列舉點對,而判斷一個點能被加到當前聯通塊中當且僅當它不滿足下列所有條件:

  • \(dis_{u,v}<dis_{w,v}\)
  • \(dis_{u,v}=dis_{w,v}\;and\;w<u\)
  • \(dis_{u,v}<dis_{w,u}\)
  • \(dis_{u,v}=dis_{w,u}\;and\;w<v\)

直接 \(n^3\) 即可。

Code
#include<bits/stdc++.h>
#define ri signed
#define pd(i) ++i
#define bq(i) --i
#define func(x) std::function<x>
namespace IO{
    char buf[1<<21],*p1=buf,*p2=buf;
    #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?(-1):*p1++
    #define debug1(x) std::cerr << #x"=" << x << ' '
    #define debug2(x) std::cerr << #x"=" << x << std::endl
    #define Debug(x) assert(x)
    struct nanfeng_stream{
        template<typename T>inline nanfeng_stream &operator>>(T &x) {
            bool f=false;x=0;char ch=gc();
            while(!isdigit(ch)) f|=ch=='-',ch=gc();
            while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=gc();
            return x=f?-x:x,*this;
        }
    }cin;
}
using IO::cin;
namespace nanfeng{
    #define FI FILE *IN
    #define FO FILE *OUT
    template<typename T>inline T cmax(T x,T y) {return x>y?x:y;}
    template<typename T>inline T cmin(T x,T y) {return x>y?y:x;}
    using ll=long long;
    static const int N=2e3+7,MOD=998244353;
    struct edge{int v,nxt;}e[N<<1];
    int first[N],dis[330][330],C[330][330],dep[N],ky[N],U[N],V[N],siz[N],n,m,k,t=1;
    ll res1,res2;
    bool bc[N];
    auto add=[](int u,int v) {
        e[t]={v,first[u]},first[u]=t++;
        e[t]={u,first[v]},first[v]=t++;
    };
    func(void(int,int)) dfs=[](int x,int fa) {
        for (ri i(first[x]),v;i;i=e[i].nxt) {
            if ((v=e[i].v)==fa) continue;
            dep[v]=dep[x]+1;
            dfs(v,x);
        }
    };
    func(void(int,int)) solve=[](int x,int fa) {
        if (bc[x]) siz[x]=1;
        for (ri i(first[x]),v;i;i=e[i].nxt) {
            if ((v=e[i].v)==fa) continue;
            dep[v]=dep[x]+1;
            solve(v,x);
            siz[x]+=siz[v];
        }
    };
    auto fpow=[](int x,int y) {
        int res=1;
        while(y) {
            if (y&1) res=1ll*res*x%MOD;
            x=1ll*x*x%MOD;
            y>>=1;
        }
        return res;
    };
    inline int main() {
        FI=freopen("tree.in","r",stdin);
        FO=freopen("tree.out","w",stdout);
        cin >> n >> m >> k;
        C[0][0]=1;
        for (ri i(1);i<=m;pd(i)) {
            C[i][0]=1;
            for (ri j(1);j<=i;pd(j)) C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
        }
        for (ri i(1);i<=m;pd(i)) cin >> ky[i],bc[ky[i]]=true;
        for (ri i(1);i<n;pd(i)) cin >> U[i] >> V[i],add(U[i],V[i]);
        for (ri i(1);i<=m;pd(i)) {
            dep[ky[i]]=0;
            dfs(ky[i],0);
            for (ri j(1);j<=m;pd(j)) dis[i][j]=dep[ky[j]];
        }
        int al=C[m][k],inv=fpow(al,MOD-2);
        solve(1,0);
        for (ri i(1);i<n;pd(i)) {
            int u=U[i],v=V[i];
            if (dep[u]>dep[v]) std::swap(u,v);
            res1+=1ll*(al-C[siz[v]][k]-C[m-siz[v]][k])*inv%MOD;
        }
        (res1%=MOD)+=MOD;
        (res1*=2)%=MOD;
        for (ri i(1);i<m;pd(i))
            for (ri j(i+1);j<=m;pd(j)) {
                const int len=dis[i][j];
                int cnt=0;
                for (ri k(1);k<=m;pd(k)) {
                    if (i==k||j==k) continue;
                    if (dis[i][k]>len||dis[k][j]>len) continue;
                    if (dis[i][k]==len&&k<j||dis[k][j]==len&&k<i) continue;
                    ++cnt;
                }
                res2+=1ll*C[cnt][k-2]*inv%MOD*len%MOD;
            }
        res2%=MOD;
        printf("%lld\n",(res1-res2+MOD)%MOD);
        return 0;
    }
}
int main() {return nanfeng::main();}