1. 程式人生 > >9.14 超級樹題解

9.14 超級樹題解

close can 個人 同時 index pan pre view isp

技術分享

技術分享

技術分享

  這道題當時dfs+打表過了3個點,還算可以。

  當時推測的是遞推/數學,然而正解是一個類似於DP的遞推。

  我們設定f[i][j]為深度為i的樹中同時存在j條邊,且所有邊無任何兩條有公共點,不必包含所有點的情況數。確實很繞也貌似沒有太大的實際意義(至少我沒有理解到),那麽我們的轉移方程便如下:

    j,k為上一步的兩個狀態,num為f[i-1][j]*f[i-1][j],即代表新樹的兩棵子樹     

    通過新節點連接某個子樹的兩個路徑:     f[i][j+k-1]+=num*(max(0ll,j*(j-1))+max(k*(k-1),0ll));     通過新節點連接分別位於兩個子樹的兩個路徑:
f[i][j+k-1]+=num*j*k*2;     無任何操作:           f[i][j+k]+=num;     將新節點連接在每一個路徑的起點(重點): f[i][j+k]+=num*(j+k)*2;
    新節點自身也算一個路徑:     f[i][j+k+1]+=num;      我們的答案也就是f[n][1],即每一條路徑,很明顯f[n][1]由f[n-1][2]轉移過來因此最多第二維到300為止,我們又要考慮他的事跡意義,因此邊界就是在300-i+1和(1<<i)-1中取min。    技術分享
 1 #include<iostream>
 2
#include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #define N 305 9 using namespace std; 10 long long n,mod; 11 long long f[N][N]; 12 int main() 13 { 14 scanf("%lld%lld",&n,&mod); 15
f[1][0]=f[1][1]=1; 16 bool yx=1; 17 for(long long i=2;i<=n;i++) 18 { 19 int to=n-(i-1)+1; 20 if(yx) 21 { 22 if(1<<(i-1)-1<0)yx=0; 23 else if(to>((1<<(i-1))-1))to=(1<<(i-1))-1; 24 } 25 for(long long j=0;j<=to;j++) 26 { 27 for(long long k=0;k<=to;k++) 28 { 29 long long num=f[i-1][j]*f[i-1][k]; num%=mod; 30 if(j+k-1>304)continue; 31 f[i][j+k-1]+=(num*(max(0ll,j*(j-1))+max(k*(k-1),0ll)))%mod; f[i][j+k-1]%=mod; 32 f[i][j+k-1]+=(num*j*k*2)%mod; f[i][j+k-1]%=mod; 33 if(j+k>304)continue; 34 f[i][j+k]+=num; f[i][j+k]%=mod; 35 f[i][j+k]+=(num*(j+k)*2)%mod; f[i][j+k]%=mod; 36 if(j+k+1>304)continue; 37 f[i][j+k+1]+=num; f[i][j+k+1]%=mod; 38 39 40 } 41 } 42 } 43 printf("%lld\n",f[n][1]%mod); 44 return 0; 45 }
View Code

  不得不說這道題真心挺難,尤其是狀態轉移方程,簡直不是人類能想出來的其實是我太弱,人家育才有7個人A了

9.14 超級樹題解