<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      P11363 [NOIP2024] 樹上遍歷 題解

      難是真的難,也值得好好整理一下。

      首先,一個圖的dfs生成樹有一個性質:不含橫插邊

      所以在本題中,因為原圖是一棵樹,所以一個點 \(u\) 周圍的邊在生成樹上一定在一條鏈上(首尾相連)。

      那么,設 \(d_i\)\(i\) 的度數,則對于一個點周圍的邊,一共有 \(d_i!\) 中排列方式。如果確定鏈的起點,則有 \((d_i-1)!\) 種情況。

      (如下圖的紅色邊)

      所以對于 \(k=1\) 的情況下,答案就是 \(\Pi_{i=1}^n (d_i-1)!\)。因為每一個鏈的起點是定的。

      我們稱用來生成這棵dfs樹的關鍵邊為 生成樹的根

      現在的問題在于,我們可能會重復計算一棵生成樹多次。

      所以現在我們需要考慮一棵生成樹可能會被那些關鍵邊給計算。

      考慮下面這個圖,這是以邊 \((5,8)\) 構成的生成樹。

      其中灰色、粉紅、橙色、綠色分別為點 \(5、3、4、1\) 相鄰的邊構成的鏈。其他的點因為度數為 \(1\) ,所以我們暫時不管他們。

      因為一個點周圍的邊要構成一條鏈,所以 \((3,4)、(4,7)\) 就不能作為這棵生成樹的根。因為從他們出發,點 \(3\) 的鏈就不是鏈了。

      所以發現,上圖的紅色邊就是可以作為這棵生成樹根的邊。

      所以可以發現,能作為同一棵生成樹的根的邊,是原樹中一個葉子節點到另一個葉子節點之間的路徑上的邊。并且與生成樹建立起雙射,就是一條這樣的路徑只對應一棵生成樹,而一棵生成樹也只對應一條這樣的路徑。

      為什么這棵生成樹一定存在這樣一個路徑可以作為根呢?

      就如上圖,我們是拿邊 \((5,8)\) 生成的。此時向 \(8\) 節點走,發現 \(8\) 節點構成的鏈的起點一定是 \((5,8)\),而其終點 \((3,5)\) 則可以作為另一個根。于是就這樣不斷地從鏈的起點到鏈的終點,而鏈的終點又是下一個鏈的起點。這樣不斷遍歷,最終會到達一個葉子節點。

      因為需要保證,從可能的根節點出發遍歷一棵樹,原本欽定為鏈的部分必須還是鏈。

      那么只要我從一個根向兩邊跳,每一次都從鏈頂跳到鏈尾,這樣就可以找到一個路徑。

      所以說,我們只需要統計有多少個 原樹中從一個葉子到另一個葉子之間的路徑使得這條路徑上至少有 \(k\) 條關鍵邊中的一條。

      那么如何統計方案數?

      我們會發現,對于一條路徑上可以作為根節點的邊的端點,其鏈的起點與終點都是定的,單個方案數為 \((d_i-2)!\)。所以方案數為 \(\Pi_{x在鏈上}(d_x-2)!\ \Pi_{x不在鏈上}(d_x-1)!\)。為了方便,我們把它改成 \(\Pi(d_i-1)!\ \Pi_{x在鏈上}(d_x-1)^{-1}\)

      這樣問題就變為:對于所有葉子到葉子的路徑,滿足其中至少一條邊為關建邊,路徑上所有點的權值乘積之和。這里讓一個點的點權為 \((d_x-1)^{-1}\)

      先把 \(n=2\) 判掉。然后對于 \(n>2\),找一個度數 \(>1\) 的位置作為根方便后續dp。

      所以考慮dp,設 \(dp_{u,0/1}\) 表示以 \(u\) 為根的子樹中,從 \(u\) 出發,不經過/經過關建邊的乘積之和。

      那么進行分類討論,對于 \(v\in son_u\)

      • \((u,v)\) 為關建邊

        則答案貢獻增加 \((dp_{u,0}+dp_{u,1})(dp_{v,0}+dp_{v,1})\)

        然后讓 \(dp_{u,1}\gets dp_{v,1}+dp_{v,0}\)

      • \((u,v)\) 不是關建邊

        則答案貢獻增加 \(dp_{v,1}(dp_{u,0}+dp_{i,1})+dp_{v,0}\times dp_{u,1}\)

        然后 \(dp_{u,1}\gets dp_{v,1}\)\(dp_{u,0}\gets dp_{v,0}\)

      然后輸出答案即可。時間復雜度 \(O(n)\)。為了方便,處理逆元我用的 \(O(n\log n)\)

      #include<bits/stdc++.h>
      using namespace std;
      const int N=2e5+5,mod=1e9+7;
      #define int long long
      int head[N],cnt,n,k,u[N],v[N],w[N];
      
      int ru[N],fac[N],inv[N],mul;
      
      int dp[N][2],ans;
      
      struct edge
      {
      	int v,nxt,w;
      }a[N<<1];
      void add(int u,int v,int w)
      {
      	a[++cnt].v=v;
      	a[cnt].w=w;
      	a[cnt].nxt=head[u];
      	head[u]=cnt;
      }
      int read()
      {
      	int x=0,f=1;char ch=getchar();
      	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
      	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
      	return x*f;
      }
      void dfs(int u,int fa)
      {
      	for(int i=head[u];i!=0;i=a[i].nxt)
      	{
      		int v=a[i].v;
      		if(v==fa) continue;
      		dfs(v,u);
      		
      		if(a[i].w) 
      		{
      			ans=(ans+(dp[v][0]+dp[v][1])*(dp[u][0]+dp[u][1])%mod)%mod;
      			dp[u][1]=(dp[u][1]+inv[ru[u]-1]*(dp[v][0]+dp[v][1]))%mod;
      		}
      		else
      		{
      			ans=(ans+dp[v][1]*(dp[u][0]+dp[u][1])%mod+dp[v][0]*dp[u][1]%mod)%mod;
      			dp[u][1]=(dp[u][1]+inv[ru[u]-1]*dp[v][1])%mod;
      			dp[u][0]=(dp[u][0]+inv[ru[u]-1]*dp[v][0])%mod;
      		}
      	}
      	if(ru[u]==1) dp[u][0]=inv[ru[u]-1];
      }
      void solve()
      {
      	n=read(),k=read();
      	
      	for(int i=1;i<=n;i++) head[i]=0,dp[i][0]=dp[i][1]=ru[i]=0;
      	cnt=0;
      	ans=0;
      	mul=1;
      	
      	for(int i=1;i<n;i++) u[i]=read(),v[i]=read(),w[i]=0;
      	for(int i=1;i<=k;i++) w[read()]=1;
      	for(int i=1;i<n;i++)
      	{
      		ru[u[i]]++;
      		ru[v[i]]++;
      		add(u[i],v[i],w[i]),add(v[i],u[i],w[i]);
      	}
      	if(n==2)
      	{
      		printf("1\n");
      		return;
      	}
      	
      	int pos=0;
      	for(int i=1;i<=n;i++) 
      	{
      		mul=mul*fac[ru[i]-1]%mod;
      		if(ru[i]>1) pos=i;
      	}
      	dfs(pos,0);
      	printf("%d\n",ans*mul%mod);
      }
      int qpow(int a,int b)
      {
      	int ans=1;
      	while(b>0)
      	{
      		if(b&1) ans=(ans*a)%mod;
      		a=(a*a)%mod;
      		b>>=1; 
      	}
      	return ans;
      }
      signed main()
      {
      	fac[0]=1,inv[0]=1;
      	for(int i=1;i<=1e5;i++) fac[i]=fac[i-1]*i%mod,inv[i]=qpow(i,mod-2);
      	int ID=read(),T=read();
      	while(T--) solve();
      	return 0;
      }
      
      
      posted @ 2025-03-10 08:38  Twilight_star  閱讀(38)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 激情动态图亚洲区域激情| 亚洲色大成网站www永久男同| 国产福利精品一区二区| 亚洲一区二区三区自拍天堂| 好男人社区影视在线WWW| 免费无码肉片在线观看| 久久精品国产久精国产| 人成午夜免费大片| 久久精品国产清自在天天线| 日韩精品亚洲精品第一页| 老子午夜精品无码| 噜噜噜噜私人影院| 色综合久久婷婷88| 麻豆精产国品一二三产| 18分钟处破好疼哭视频在线观看| 色综合色综合色综合频道| 亚洲天堂在线观看完整版| 国产男女爽爽爽免费视频| 久久九九日本韩国精品| 国产69精品久久久久99尤物| 阿合奇县| ww污污污网站在线看com| 狠狠婷婷色五月中文字幕| 精品不卡一区二区三区| 成人国产亚洲精品一区二区| 婷婷久久香蕉五月综合加勒比| 国内精品久久久久精免费| 无码av天天av天天爽| 伊人成伊人成综合网222| 国产suv精品一区二区| 国产精品天天看天天狠| 人妻少妇精品中文字幕| 中文字幕日韩精品有码视频| 亚洲精品无码你懂的| 国产对白熟女受不了了| 亚洲精品麻豆一区二区| 亚洲AV无码东方伊甸园| 亚洲一区国色天香| 国产在线98福利播放视频 | 西西大胆午夜人体视频| 久久青草国产精品一区|