| 
分 類 大 討 論 
 # 題面給定一棵 \(n\) 個(gè)點(diǎn)的樹,將其復(fù)制 \(m\) 次得到 \(m+1\) 棵樹,依次編號(hào)為 \(0\sim m\)。記編號(hào)為 \(i\) 的樹的節(jié)點(diǎn) \(j\) 為 \((i,j)\)。 令所有 \(m+1\) 棵樹上的邊都是雙向邊,另外對(duì)于每個(gè) \(i\in[1,m]\),指定 \(A_i,B_i\),連一條有向邊:\((i-1,A_i)\to(i,B_i)\)。這樣得到一個(gè)連通的圖。 兩個(gè)玩家 P,Q 在這個(gè)圖上玩游戲:最初 \((0,1)\) 上有一個(gè)棋子,每個(gè)玩家輪流操作,將棋子挪到相鄰的點(diǎn)上。規(guī)定不能挪到已經(jīng)到過(guò)的點(diǎn),無(wú)法繼續(xù)移動(dòng)的玩家輸?shù)?a href="http://www." target="_blank">游戲。 P 為先手,求有多少種 \(A_1,B_1,A_2,B_2,\dots,A_m,B_m\) 的方案使得 P 必勝。 數(shù)據(jù)規(guī)模:\(1\le n\le10^5,1\le m\le10^{18}\)。 
 # 解析可以看出,從樹 \(i-1\) 經(jīng)過(guò)有向邊到達(dá)樹 \(i\) 的點(diǎn) \(B_i\),就可以看成「把 \(B_i\) 作為第 \(i\) 棵樹的根,只能向子樹方向走或走有向邊」這種問(wèn)題。 走有向邊比較復(fù)雜,先不考慮。我們先嘗試求出以點(diǎn) \(1\) 為根,只能向子樹方向走,每個(gè)點(diǎn)是必勝還是必?cái)?;然后通過(guò)換根 DP 可以得到每個(gè)點(diǎn)為根的答案。 我們可以非常輕松地做一個(gè) DP,求出從點(diǎn) \(i\) 出發(fā)只能朝子樹方向走,先手是否必勝,記為 \(h_i\)(\(h_i=0\) 即必?cái)。?span id="opkdopnojk"    class="math inline">\(h_i=1\) 即必勝)。根據(jù)基礎(chǔ)的博弈論知識(shí),當(dāng) \(u\) 能轉(zhuǎn)移到的所有 \(h_v\) 都為 \(1\),則 \(h_u=0\);只要有一個(gè) \(h_v=0\),則 \(h_u=1\)。 然后考慮加上有向邊 \((i,s)\to (i+1,t)\)。這相當(dāng)于給 \(s\) 新增了一種轉(zhuǎn)移方案,不妨討論一下這個(gè)新增的轉(zhuǎn)移會(huì)對(duì) \(h_s\) 造成什么影響: 
不難發(fā)現(xiàn)造成的影響只與「\((i+1,t)\) 是必勝還是必?cái) 褂嘘P(guān),而與 \(t\) 具體是哪個(gè)點(diǎn)無(wú)關(guān);若 \(t\) 是必?cái)↑c(diǎn):
若 \(h_s=1\),則 \(s\) 仍然必勝;若 \(h_s=0\),則 \(s\) 變?yōu)楸貏賾B(tài)。若 \(t\) 是必勝點(diǎn):
若 \(h_s=1\),則 \(s\) 仍然必勝;若 \(h_s=0\),則 \(s\) 仍然必?cái) ?/li>
 于是我們?cè)俣x一個(gè) \(g_{u,0/1}\),表示 \(u\) 的子樹內(nèi)有一條連向下一棵樹的有向邊,加上這條有向邊后,\(u\) 的狀態(tài)為 \(0\)(必?cái)。? \(1\)(必勝)的方案數(shù)。 雖然在計(jì)算下一棵樹的答案之前,我們無(wú)法求出 \(g_{u,0/1}\) 的具體值,但是根據(jù)之前的討論,我們知道 \(g\) 的值只與「下一棵樹在每個(gè)連邊狀態(tài)下,必?cái)↑c(diǎn)的數(shù)量的總和/必勝點(diǎn)的數(shù)量的總和」有關(guān),因?yàn)橹挥羞@兩個(gè)值決定了 \(t\) 可取的方案數(shù)。 不妨記 \(T_{i,0},T_{i,1}\) 分別表示第 \(i\) 棵樹在所有狀態(tài)下必?cái)↑c(diǎn)/必勝點(diǎn)數(shù)量的總和。那么 \(g_{u,k}\) 可以表示為: \[g_{u,k}=g_{u,k,0}T_{i+1,0}+g_{u,k,1}T_{i+1,1}
\] 因?yàn)橐豢脴渲粫?huì)連出一條有向邊,所以所有的 \(g_{u,k}\) 都會(huì)是這個(gè)形式——我們可以 DP 計(jì)算出每個(gè)系數(shù) \(g_{u,k,0/1}\)。 這只是以 \(1\) 為根的情況,我們?nèi)匀豢梢該Q根求出「以每個(gè)點(diǎn)為根,要使根為必勝/必?cái)∮卸嗌俜N方案」,記作 \(f_{u,0/1}\)。和 \(g\) 一樣,\(f\) 也是關(guān)于 \(T_{i+1,0},T_{i+1,1}\) 的式子。 然后我們可以得到: \[\begin{aligned}
T_{i,0}&=\sum_{u=1}^nf_{u,0}=AT_{i+1,0}+BT_{i+1,1}\T_{i,1}&=\sum_{u=1}^nf_{u,1}=CT_{i+1,0}+DT_{i+1,1}
\end{aligned}
\] 不難發(fā)現(xiàn),對(duì)于每棵樹,系數(shù) \(A,B,C,D\) 是相同的(每棵樹的樹形是一樣的,那么上述DP的轉(zhuǎn)移也都是一樣的,不一樣的是 \(T_{i+1,0},T_{i+1,1}\),但這和系數(shù)無(wú)關(guān))。 即: \[\begin{bmatrix}T_{i,0}\\T_{i,1}\end{bmatrix}
=\begin{bmatrix}A&B\\C&D\end{bmatrix}
\times\begin{bmatrix}T_{i+1,0}\\T_{i+1,1}\end{bmatrix}
\] 計(jì)算的終點(diǎn)在第 \(m\) 棵樹,因?yàn)樗鼪](méi)有連出的有向邊,可以直接計(jì)算出 \(T_{m,0},T_{m,1}\)。 然后這個(gè)式子就可以矩陣加速,最終復(fù)雜度 \(\mathcal{O}(n+\log m)\)。 唯一的麻煩點(diǎn)在于 \(g\) 的換根 DP。要選擇一個(gè)子樹 \(v\)(或者 \(u\) 本身)連出一條有向邊,則需要考慮除去子樹 \(v\),剩下的子樹中是否有必?cái)B(tài),若有必?cái)B(tài),則 \(u\) 一定是必勝態(tài),與 \(v\) 無(wú)關(guān);否則 \(u\) 的狀態(tài)取決于 \(v\) 連邊后的狀態(tài)。 的確有優(yōu)美的實(shí)現(xiàn)方法,但是我考場(chǎng)上嘗試少寫點(diǎn)代碼結(jié)果調(diào)到結(jié)束都還沒(méi)過(guò)大樣例……還不如直接分類大討論…… 
 # 源代碼不建議借鑒這份代碼 (;′⌒`) 
點(diǎn)擊展開/折疊代碼開幕雷擊[doge] /*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
template<class T>inline T rin(T &r){
	int b=1,c=getchar();r=0;
	while(c<'0' || '9'<c) b=c=='-'?-1:b,c=getchar();
	while('0'<=c && c<='9') r=(r<<1)+(r<<3)+(c^'0'),c=getchar();
	return r*=b;
}
const int N=1e5+10,MOD=1e9+7;
typedef long long llong;
#define con(type) const type &
inline int add(con(int)a,con(int)b){return a+b>=MOD?a+b-MOD:a+b;}
inline int sub(con(int)a,con(int)b){return a-b<0?a-b+MOD:a-b;}
inline int mul(con(int)a,con(int)b){return int(1ll*a*b%MOD);}
inline int ina_pow(con(int)a,con(int)b){return b?mul(ina_pow(mul(a,a),b>>1),(b&1)?a:1):1;}
inline int inv(con(int)key){return ina_pow(key,MOD-2);}
struct Graph{
	int head[N],to[N<<1],nxt[N<<1],ncnt;
	void addEdge(con(int)u,con(int)v){
		int p=++ncnt,q=++ncnt;
		to[p]=v,nxt[p]=head[u],head[u]=p;
		to[q]=u,nxt[q]=head[v],head[v]=q;
	}
	inline int operator [](con(int)u){return head[u];}
}gr;
struct Data{
	int sum0,sum1;
	Data(){sum0=sum1=0;}
	Data(con(int)_sum0,con(int)_sum1):sum0(_sum0),sum1(_sum1){}
	Data operator +(con(Data)b)const{return Data(add(sum0,b.sum0),add(sum1,b.sum1));}
	Data operator -(con(Data)b)const{return Data(sub(sum0,b.sum0),sub(sum1,b.sum1));}
	Data operator *(con(int)d)const{return Data(mul(sum0,d),mul(sum1,d));}
	friend Data operator *(con(int)b,con(Data)a){return Data(mul(a.sum0,b),mul(a.sum1,b));}
	void operator +=(con(Data)b){(*this)=(*this)+b;}
	void operator -=(con(Data)b){(*this)=(*this)-b;}
	void operator *=(con(int)b){(*this)=(*this)*b;}
	int value(con(int)c0,con(int)c1){return add(mul(sum0,c0),mul(sum1,c1));}
	// void debug()const{printf("(%d,%d)",sum0,sum1);}
};
int n,tot_win,tot_los;
llong m;
Data g[N][2],f[N][2];
int h[N],zerotyp[N];
// int wtfdebug[N];
// h[i]=0 loser ; h[i]=1 winner
void dpDFS(con(int)u,con(int)fa){
	int cnt_los=0;
	for(int it=gr[u];it;it=gr.nxt[it]){
		int v=gr.to[it];
		if(v==fa) continue;
		dpDFS(v,u);
		cnt_los+=!h[v];
	}
	h[u]=cnt_los>0;
	for(int it=gr[u];it;it=gr.nxt[it]){
		int v=gr.to[it];
		if(v==fa) continue;
		if(cnt_los-(!h[v])) // already win
			g[u][1]+=g[v][0]+g[v][1];
		else{
			g[u][0]+=g[v][1];
			g[u][1]+=g[v][0];
		}
	}
	if(cnt_los) g[u][1]+=Data(1,1);
	else g[u][0]+=Data(0,1),g[u][1]+=Data(1,0);
}
void rootDFS(con(int)u,con(int)fa,con(int)ph,Data *pg){
	int cnt_los=!ph;
	for(int it=gr[u];it;it=gr.nxt[it]){
		int v=gr.to[it];
		if(v==fa) continue;
		cnt_los+=!h[v];
	}
	tot_win+=cnt_los>0;
	tot_los+=cnt_los==0;
	// wtfdebug[u]=cnt_los;
	if(!cnt_los){
		Data gwin=pg[0],glos=pg[1];
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			gwin+=g[v][0],glos+=g[v][1];
		}
		gwin+=Data(1,0),glos+=Data(0,1);
		f[u][0]=glos,f[u][1]=gwin;
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			Data nowg[2]={glos-g[v][1],gwin-g[v][0]};
			rootDFS(v,u,0,nowg);
		}
	}
	else if(cnt_los==1){
		Data gwin,gwinsp,glos,glossp;
		// sp: if transform to v ('h[v]==0')
		if(!ph) gwin+=pg[0],glos=pg[1];
		else{
			gwinsp+=pg[0],glossp+=pg[1];
			gwin+=pg[0]+pg[1];
		}
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			if(!h[v]) gwin+=g[v][0],glos+=g[v][1];
			else{
				gwinsp+=g[v][0],glossp+=g[v][1];
				gwin+=g[v][0]+g[v][1];
			}
		}
		gwinsp+=Data(1,0),glossp+=Data(0,1);
		gwin+=Data(1,1);
		f[u][0]=glos,f[u][1]=gwin;
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			Data nowg[2];
			if(h[v]){
				nowg[0]=glos;
				nowg[1]=gwin-(g[v][0]+g[v][1]);
				rootDFS(v,u,1,nowg);
			}
			else{
				nowg[0]=glossp,nowg[1]=gwinsp;
				rootDFS(v,u,0,nowg);
			}
		}
	}
	else if(cnt_los==2){
		Data gwin,glos,gwinsp,glossp;
		if(!ph) gwin+=pg[0]+pg[1],gwinsp+=pg[0],glossp+=pg[1];
		else gwin+=pg[0]+pg[1],gwinsp+=pg[0]+pg[1];
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			if(!h[v]) gwin+=g[v][0]+g[v][1],gwinsp+=g[v][0],glossp+=g[v][1];
			else gwin+=g[v][0]+g[v][1],gwinsp+=g[v][0]+g[v][1];
		}
		gwin+=Data(1,1);
		gwinsp+=Data(1,1);
		f[u][0]=glos,f[u][1]=gwin;
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			Data nowg[2];
			if(!h[v]){
				nowg[0]=glossp-g[v][1];
				nowg[1]=gwinsp-g[v][0];
				rootDFS(v,u,1,nowg);
			}
			else{
				nowg[0]=glos,nowg[1]=gwin-g[v][0]-g[v][1];
				rootDFS(v,u,1,nowg);
			}
		}
	}
	else{
		Data gwin,glos;
		gwin+=pg[0]+pg[1];
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			gwin+=g[v][0]+g[v][1];
		}
		gwin+=Data(1,1);
		f[u][0]=glos,f[u][1]=gwin;
		for(int it=gr[u];it;it=gr.nxt[it]){
			int v=gr.to[it];
			if(v==fa) continue;
			Data nowg[2];
			nowg[0]=glos,nowg[1]=gwin-g[v][0]-g[v][1];
			rootDFS(v,u,1,nowg);
		}
	}
}
void multi(int a[2][2],int b[2][2]){
	int c[2][2]={};
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			for(int k=0;k<2;k++)
				c[i][j]=add(c[i][j],mul(a[i][k],b[k][j]));
	for(int i=0;i<2;i++)
		for(int j=0;j<2;j++)
			a[i][j]=c[i][j];
}
int main(){
	// freopen("tree\\tree3.in","r",stdin);
	rin(n),rin(m);
	for(int i=1,u,v;i<n;i++)
		gr.addEdge(rin(u),rin(v));
	dpDFS(1,0);
	Data nowg[2];
	rootDFS(1,0,1,nowg);
	Data trlos,trwin;
	for(int i=1;i<=n;i++)
		trlos+=f[i][0],trwin+=f[i][1];
	int mat[2][2]={{trlos.sum0,trlos.sum1},{trwin.sum0,trwin.sum1}},res[2][2]={{1,0},{0,1}};
	m--;
	while(m){
		if(m&1) multi(res,mat);
		multi(mat,mat),m>>=1;
	}
	int res_win=add(mul(res[1][0],tot_los),mul(res[1][1],tot_win)),
		res_los=add(mul(res[0][0],tot_los),mul(res[0][1],tot_win));
	printf("%d\n",f[1][1].value(res_los,res_win));
	return 0;
}
 
 THE ENDThanks for reading!
琴弦上羽徵角商角 羽徵角商角不知何時(shí)才能到下一闋
 琴聲下你是月上月 你是月上月
 比長(zhǎng)夜中月色更皎潔
 琴瑟間曲辭疊上疊 曲辭疊上疊
 游走過(guò)的繞梁聲不停歇
 讓我的心
 似浮萍隨 漣漪搖曳
 ——《琴弦上(vocaloid)》By 樂(lè)正綾/赤羽/星葵 > Link 琴弦上-Bilibili |