Loading

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();}
posted @ 2021-11-02 20:42  ナンカエデ  阅读(57)  评论(0编辑  收藏  举报