1. 程式人生 > >[國家隊集訓]最短路

[國家隊集訓]最短路

概念 tor har 由於 note i++ 連通 turn 完全

https://www.zybuluo.com/ysner/note/1253665

題面

考慮一張\(n\)個點的無向完全圖,總共有\(\frac{n*(n?1)}{2}\)條邊。每條邊有\(p\)的概率存在,\(1-\frac{p}{10^6}\)的概率不存在。
現在等概率在\(n\)個點中隨機選一個,求\(1\)號點到它的最短距離(經過的邊數)。
如果\(1\)號點和它不連通,則認為答案是\(10^9\) 。求期望答案。

  • \(50pts\ n\leq100\)
  • \(100pts\ n\leq400\)

    解析

    不難發現,在當前題目條件下,除了\(1\)節點,其它點期望是一樣的。

可以考慮從\(1\)號點出發,由近到遠地統計各個點的期望。(然而我考場上是反過來想的,於是逃不過後效性這個大坑)
據說這個概念叫\(BFS\)

樹???(深度等於距離)

\(50pts\)算法

一個想法是設\(dp[i][sum][s]\)表示當前統計到到第\(i\)層(離\(1\)號點距離為\(i\))、已經統計了\(j\)個點的答案、用其中的\(s\)個點與下一層的\(t\)(枚舉)個點相連(往下拓展)的任意一點的期望。
(至於為什麽這麽多維,嘗試列下轉移式就知道了)

那麽,枚舉一下\(t\),轉移是這樣的

if(i+1==n-1) 
add(f[i+1][sum+t][t],1ll*f[i][sum][s]*lk[s][t]%mod*C[n-sum][t]%mod);
else 
add(f[i+1][sum+t][t],1ll*f[i][sum][s]*lk[s][t]%mod*dlk[s][n-sum-t]%mod*C[n-sum][t]%mod);

\(lk[s][t]\)表示\(t\)個點起碼被\(s\)中一個點連了的概率,即\((1-(1-\frac{p}{10^6})^s)^t\)
\(dlk[s][t]\)表示\(s\)個點與\(t\)個點完全沒邊的概率,即\((1-\frac{p}{10^6})^{st}\)

統計答案,就是把當前層所有點的距離(就是層編號)乘上概率,得到當前層的期望,加入答案。
註意不連通這一情況還要單獨乘概率

for(int d = 0; d <= n-1; d ++)
  for(int sum = 1; sum <= n; sum ++)
    for(int s = 1; s <= sum; s ++)
      if(f[d][sum][s]) 
      {
        add(Ans,1ll*f[d][sum][s]*s%mod*d%mod) ;
        add(Ans,1ll*f[d][sum][s]*(n-sum)%mod*dlk[s][n-sum]%mod*(1e9)%mod);
    }

復雜度\(O(n^4)\)

\(100pts\)算法

發現由於每次向下只轉移一層,那個\(i\)沒有什麽卵用(但是它要統計進答案)。
考慮把它弄掉。

我們不枚舉\(i\)了,並把這一維從\(DP\)狀態中刪掉。

註意到(\(p\)為概率,\(E\)為期望)
\(ans\)
\(=\sum P*(dis+1)\)
\(=\sum P*dis+\sum P\)
\(=\sum E+\sum P\)

則我們可以把\(dp\)數組分為兩個,\(sum\)表示當前已經統計了\(sum\)個點的答案,\(j\)表示將用其中\(j\)個點與下一層點相連。則\(f[sum][j]\)表示該狀態下的概率和,\(g[sum][j]\)表示該狀態的期望和(即概率乘期望)。

於是每層任意一點對答案產生的貢獻轉變為\(f[sum][j]+g[sum][j]\)

怎麽轉移\(f\)\(g\)
枚舉\(t\),乘上該層連通的概率和方案數(選出\(n-sum\)中的\(k\)個點),
\(f[sum][j]\)就可以轉移給\(f[sum+k][k]\),
\(g[num][j]+f[sum][j]\)就可以轉移給\(g[sum+k][k]\)

最後\(ans\)要乘上\(n-1\)。(沒錯,\(50pts\)算法統計了,這裏沒統計)
復雜度\(O(n^3)\)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int mod=998244353,N=500,M=1e6;
ll n,p,inv,ans,f[N][N],g[N][N],C[N][N],np,b[N],lk[N][N],dlk[N][N];
bool vis[N];
il ll gi()
{
   re ll x=0,t=1;
   re char ch=getchar();
   while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
   if(ch==‘-‘) t=-1,ch=getchar();
   while(ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-48,ch=getchar();
   return x*t;
}
il ll ksm(re ll S,re ll o)
{
  re ll T=S;S=1;
  while(o)
    {
      if(o&1) S=S*T%mod;
      T=T*T%mod;
      o>>=1;
    }
  return S;
}
il void Pre()
{
  b[0]=1;fp(i,1,n) b[i]=b[i-1]*np%mod;
  fp(i,0,n) fp(j,0,n) lk[i][j]=ksm(mod+1-b[i],j);
  fp(i,0,n) fp(j,0,n) dlk[i][j]=ksm(np,i*j);
  fp(i,0,n)
    {
      C[i][0]=1;
      fp(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    }
}
il void DP()
{
  f[1][1]=1;g[1][1]=0;
  fp(sum,1,n-1)
    fp(j,1,sum)
    if(f[sum][j])
      {
    (ans+=(mod+1-b[j])*(f[sum][j]+g[sum][j])%mod)%=mod;
    (ans+=dlk[n-sum][j]*f[sum][j]%mod*((ll)(1e9)%mod)%mod)%=mod;
    fp(k,1,n-sum-1)
      {
        re ll xs=lk[j][k]*dlk[j][n-sum-k]%mod*C[n-sum-1][k]%mod;
        (f[sum+k][k]+=xs*f[sum][j]%mod)%=mod;
        (g[sum+k][k]+=xs*((f[sum][j]+g[sum][j])%mod)%mod)%=mod;
      }
      }
}
int main()
{
  n=gi();p=gi();inv=ksm(M,mod-2);
  np=(M-p)*inv%mod;
  Pre();DP();
  printf("%lld\n",ans*(n-1)%mod*ksm(10,6*n*n)%mod);
  return 0;
}

[國家隊集訓]最短路