乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      「SOL」星際迷航(LOJ)

       頭號(hào)碼甲 2022-08-07 發(fā)布于北京

      分 類(lèi) 大 討 論


      # 題面

      給定一棵 \(n\) 個(gè)點(diǎn)的樹(shù),將其復(fù)制 \(m\) 次得到 \(m+1\) 棵樹(shù),依次編號(hào)為 \(0\sim m\)。記編號(hào)為 \(i\) 的樹(shù)的節(jié)點(diǎn) \(j\)\((i,j)\)。

      令所有 \(m+1\) 棵樹(shù)上的邊都是雙向邊,另外對(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}\)


      # 解析

      可以看出,從樹(shù) \(i-1\) 經(jīng)過(guò)有向邊到達(dá)樹(shù) \(i\) 的點(diǎn) \(B_i\),就可以看成「把 \(B_i\) 作為第 \(i\) 棵樹(shù)的根,只能向子樹(shù)方向走或走有向邊」這種問(wèn)題。

      走有向邊比較復(fù)雜,先不考慮。我們先嘗試求出以點(diǎn) \(1\) 為根,只能向子樹(shù)方向走,每個(gè)點(diǎn)是必勝還是必?cái)?;然后通過(guò)換根 DP 可以得到每個(gè)點(diǎn)為根的答案。

      我們可以非常輕松地做一個(gè) DP,求出從點(diǎn) \(i\) 出發(fā)只能朝子樹(shù)方向走,先手是否必勝,記為 \(h_i\)\(h_i=0\) 即必?cái)。?span id="mrlkstq" 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\) 的子樹(shù)內(nèi)有一條連向下一棵樹(shù)的有向邊,加上這條有向邊后,\(u\) 的狀態(tài)為 \(0\)(必?cái)。? \(1\)(必勝)的方案數(shù)。

      雖然在計(jì)算下一棵樹(shù)的答案之前,我們無(wú)法求出 \(g_{u,0/1}\) 的具體值,但是根據(jù)之前的討論,我們知道 \(g\) 的值只與「下一棵樹(shù)在每個(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\) 棵樹(shù)在所有狀態(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)橐豢脴?shù)只會(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ù),系數(shù) \(A,B,C,D\) 是相同的(每棵樹(shù)的樹(shù)形是一樣的,那么上述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\) 棵樹(shù),因?yàn)樗鼪](méi)有連出的有向邊,可以直接計(jì)算出 \(T_{m,0},T_{m,1}\)。

      然后這個(gè)式子就可以矩陣加速,最終復(fù)雜度 \(\mathcal{O}(n+\log m)\)。

      唯一的麻煩點(diǎn)在于 \(g\) 的換根 DP。要選擇一個(gè)子樹(shù) \(v\)(或者 \(u\) 本身)連出一條有向邊,則需要考慮除去子樹(shù) \(v\),剩下的子樹(shù)中是否有必?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)上嘗試少寫(xiě)點(diǎn)代碼結(jié)果調(diào)到結(jié)束都還沒(méi)過(guò)大樣例……還不如直接分類(lèi)大討論……


      # 源代碼

      不建議借鑒這份代碼 (;′⌒`)

      點(diǎn)擊展開(kāi)/折疊代碼

      開(kāi)幕雷擊[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 END

      Thanks for reading!

      琴弦上羽徵角商角 羽徵角商角
      不知何時(shí)才能到下一闋
      琴聲下你是月上月 你是月上月
      比長(zhǎng)夜中月色更皎潔
      琴瑟間曲辭疊上疊 曲辭疊上疊
      游走過(guò)的繞梁聲不停歇
      讓我的心
      似浮萍隨 漣漪搖曳

      ——《琴弦上(vocaloid)》By 樂(lè)正綾/赤羽/星葵

      > Link 琴弦上-Bilibili

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類(lèi)似文章 更多