首页 > 其他分享 >P6803 星际迷航 题解

P6803 星际迷航 题解

时间:2024-10-29 11:10:50浏览次数:6  
标签:状态 必败 题解 迷航 必胜 int cases P6803 节点

P6803 星际迷航 题解

题目大意

给定一颗 \(N\) 个节点的树。这样的树有 \(D+1\) 层,编号从 \(0\) 到 \(D\)。对于 \(i=0,1,\dots,D-1\),需要选择第 \(i\) 层的任意一个节点向第 \(i+1\) 层的任意一个节点连一条有向边。最初人在第 \(0\) 层图的 \(1\) 号节点。两个玩家交替选择下一步向哪走,要求走到的点 \(v\) 之前没有到过,且与当前节点 \(u\) 相邻或有一条有向边从 \(u\) 指向 \(v\)。如果到一个玩家选择时,不能走到任何没有到过的节点,这个玩家就输了。问有多少种连有向边的方案能使得先手必胜。

Solve

首先,如果从一棵树的节点 \(u\) 开始走,那么将这棵树的根定为 \(u\) 之后,走出的合法路径一定是一条从浅往深走的链。所以如果第 \(i\) 层和第 \(i+1\) 层间的有向边指向节点 \(u\),那么相当于把第 \(i+1\) 层的树的根定为 \(u\)。

连接这条有向边之后,会根据节点 \(u\) 是必胜还是必败,对这条边起点的状态做出一定修改。所以,我们并不关心这条有向边指向的是哪个节点,只关心从指向的那个节点开始,先手是必胜还是必败。

由此,我们设计如下状态:设 \(f(i,1/0)\) 表示考虑了 \(i\) 层树,从第 1 棵树的任意节点开始走,使得先手必胜 / 必败,这样的连边的总方案数。

考虑我把在这 \(i\) 层树前面再添一层树(记为第 0 棵树),即把原来的第 1 棵树连在第 0 棵的某个节点下。 \(f(i,0/1)\) 如何转移到 \(f(i+1,0/1)\)?

记新连的这条有向边为 \((u,v)\)。第 \(0\) 棵树的根为 \(s\)。

对于 \(f(i+1,1)\),我要求最后先手必胜。

  • 如果 \(s\) 原本是必胜的,我要求 \((u,v)\) 不能改变 \(s\) 的状态;
  • 否则,我要求必须改变 \(s\) 的状态。

对于 \(f(i+1,0)\),类似。

接下来考虑什么样的 \((u,v)\) 能使 \(s\) 的状态发生改变。显然当这 \(i\) 层树在从 \(v\) 开始时是必胜态时,\((u,v)\) 一定不会改变 \(s\) 的状态,即:\(u\) 只有连向一个必败点,\((u,v)\) 才会改变 \(s\) 的状态。

所以我们设 \(g(s)\) 表示在以 \(s\) 为根时,有多少个节点 \(u\) 满足从 \(u\) 向一个必败点连边后,\(s\) 的状态会发生改变。

如果求出来 \(g(u)\),我们根据 \(s\) 原本的状态和要转移到的状态的异同,就能知道是否要求 \((u,v)\) 必须改变 \(s\) 的状态,就有了如下状态转移式:

\[f(i+1,1)\longleftarrow\sum\limits_{s=1}^n \begin{cases} g(s)f(i,0),&h(s)=0\\ n(f(i,0)+f(i,1))-g(s)f(i,0).&h(s)=1 \end{cases}\\ f(i+1,0)\longleftarrow\sum\limits_{s=1}^n \begin{cases} g(s)f(i,0),&h(s)=1\\ n(f(i,0)+f(i,1))-g(s)f(i,0).&h(s)=0\\ \end{cases} \]

其中,\(h(s)\) 表示在原树中,以 \(s\) 为根是先手必胜还是必败。\(n(f(i,0)+f(i,1))\) 为这 \(i+1\) 层间的总连边方案。

系数为定值,并且 \(i\) 的范围很大,但第二维状态很少,所以考虑矩阵快速幂加速。

求出 \(f(D,0/1)\) 之后,根据 \(h(1)\) 来计算最终答案即可。

\(h\) 的换根比较简单。问题是 \(g\) 怎么求。

先考虑若以 1 为根,如何求 \(g(1)\),即朴素树形 dp。

设 \(g'_s(u)\) 表示以 \(s\) 为根时,\(u\) 的子树内有多少节点满足向必败点连边后能改变 \(u\) 的状态。分情况讨论。

  • 若 \(u\) 子树内必败,那么 \(g'_s(u)\) 即为所有儿子 \(g'\) 的和,再加上 1,即自己直接连向必败点的方案。
  • 否则若 \(u\) 的儿子里只有一个必败,那个儿子记为 \(u'\),那么 \(g'_s(u)=g'_s(u')\)。

至于换根,记 \(w(u,0/1)\) 表示以 \(u\) 为根时,\(u\) 儿子里必败 / 必胜点的 \(g\) 值的和,\(w'_{s}(u,0/1)\) 表示以 \(s\) 为根时,\(u\) 儿子里必败 / 必胜点的\(g\) 值的和。那么 \(g'\) 的转移可以形式化地表示为:

\[g'_s(u)= \begin{cases} w'_s(u,0)+1,&h'_s(u)=0\\ w'_s(u,1),&h'_s(u)=1\\ 0.&\text{Otherwise} \end{cases} \]

其中,\(h'(u)\) 表示以 1 为根时,\(u\) 的儿子内必败点个数。之所以弃掉原本必胜 / 必败的意义,是为了方便转移。

接下来考虑如何换根求 \(g(u)\)。

当根从 \(u\) 换到 \(v\) 时,首先要把 \(v\) 的贡献从 \(w(u),h(u)\) 里刨掉得到 \(w'_v(u),h'_v(u)\),之后根据上面的式子,计算 \(g'_v(u)\),用 \(h'_v(u),g'_v(u)\) 更新 \(h(v),w(v)\)。

感觉重点在这个 \(w\) 的定义,很巧妙,省去了很多讨论。

Code

void F(int u,int fa)
{
	for(int v:e[u])
		if(v!=fa)
			F(v,u),h[u]+=!h[v],w[u][h[v]>0]+=g[v];
	if(h[u]==1)	g[u]=w[u][0];
	if(!h[u])	g[u]=w[u][1]+1;
}
void G(int u,int fa)//换根
{
	if(!h[u])	cnt=-~cnt;
	int _h,_g,_w[2];
	for(int v:e[u])
		if(v!=fa)
		{
			_w[0]=w[u][0],_w[1]=w[u][1];
			_h=h[u]-!h[v];_w[h[v]>0]-=g[v];
			if(_h>1)	_g=0;
			else if(_h)	_g=_w[0];
			else	_g=_w[1]+1;
			h[v]+=!_h;w[v][_h>0]+=_g;
			if(h[v]>1)	g[v]=0;
			else if(h[v])	g[v]=w[v][0];
			else	g[v]=w[v][1]+1;
			G(v,u);
		}
}
struct mat{矩乘板子}a;
signed main()
{
	n=read();m=read();
	for(int i=1,u,v;i<n;i=-~i)
		u=read(),v=read(),
		e[u].push_back(v),e[v].push_back(u);
	F(1,0);G(1,0);
	for(int i=1;i<=n;i=-~i)//根据换根求得的 g 计算转移矩阵
		if(h[i])
			add(a.a[0][0],g[i]),
			add(a.a[0][1],n-g[i]),add(a.a[1][1],n);
		else
			add(a.a[0][1],g[i]),
			add(a.a[0][0],n-g[i]),add(a.a[1][0],n);
	a=a^(m-1);
	int x=(1ll*cnt*a.a[0][0]+1ll*(n-cnt)*a.a[1][0])%MOD;
	int y=(1ll*cnt*a.a[0][1]+1ll*(n-cnt)*a.a[1][1])%MOD;
	if(h[1])	ans=(1ll*n*(x+y)-1ll*g[1]*x)%MOD;//若以 1 为根必胜,我要求不能改变根的状态
	else	ans=1ll*g[1]*x%MOD;//否则要求必须改变
	return printf("%d",ans),0;
}

标签:状态,必败,题解,迷航,必胜,int,cases,P6803,节点
From: https://www.cnblogs.com/sorato/p/18512568

相关文章

  • CF1249F Maximum Weight Subset 题解 / 长链剖分复习
    CF1249FMaximumWeightSubset题解题目大意给定一个\(n\)个节点的树,每个节点有点权\(a_i\)。从中选出若干节点,要求这些点两两之间距离大于给定常数\(k\),使得点权和最大。Solve给出一种线性做法。前置知识:长链剖分优化DP。考虑一个DP:设\(f(u,d)\)表示在\(u\)的子......
  • Codeforces Round 982 (Div. 2) 10.26 (ABC)题解
    CodeforcesRound982(Div.2)10.26(ABC)题解A.RectangleArrangement数学(math)题意:有一个无限长宽的方形网格,初始为白色,现在有\(n\)个印章,每个印章有自己的宽\(w_i\)和高\(h_i\)。印章会使得网格涂色,变成黑色。这\(n\)个印章都需要使用一次,需要求解出最后网格中黑色......
  • Codeforces Round 982 (Div. 2) 题解(A-D)
    目录A思路codeB思路codeC思路卡题原因codeD思路未ac原因codeCodeforcesRound982(Div.2)A思路因为图形可以重叠,所以答案就是最长的长和最长的宽组成的矩形周长.codevoidfunc(void){ intn; cin>>n; intl=0,r=0; while(n--) { intx,y; cin>>x>>y......
  • 题解:CF1666J Job Lookup
    被迫来写篇题解。首先,第一个要求我们只需要在递归构造的时候保证子树对应区间连续即可,现在考虑第二个要求。就题目中的二叉树而言,想要确定其结构,我们只需要关注这段区间,即这棵子树根节点的编号,又因为子树区间连续,所以我们不难想到区间动态规划。设\(dp_{l,r}\)表示\(l\simr......
  • 【最新华为OD机试E卷-支持在线评测】机器人活动区域(200分)多语言题解-(Python/C/Java
    ......
  • CF1738F 题解
    blog。duel的时候对上了脑电波很快过了,记一下这种我本来完全不会的题。肯定是搞掉平方。把\(n_c\)移到左边:\(\dfrac{\sum\limits_{u\inS}deg_u}{|S|}=\text{平均数}\le|S|\)。然后直接放缩左边,于是一个充分条件是:\[\max\limits_{u\inS}deg_u\le|S|\]考虑构造合法解。......
  • CSP-S 2024 游记/题解
    CSP-S2024去年S打成屎了,我要蓝√!!!!!!!!CSP怎么能不CS?CS了一个上午,顺便背了下快读。进厂!看见了退役的同学在做志愿者,祝他未来可期吧。也希望自己能够超常发挥。垃圾鼠标真难用。很好,全是传统题。只有T2开了两秒。T1,这不排个序然后优先队列乱搞就好了。5min过了,NOIP我来......
  • Removing People 题解
    前言题目链接:Atcoder;洛谷。题意简述\(n\)人站成一个圆圈,按顺时针方向依次为\(1,2,\cdots,n\)。每个人面对的方向由长度为\(n\)的字符串\(S\)给出。对于第\(i\)个人,如果\(S_i=\texttt{L}\),则\(i\)面向逆时针方向。如果\(S_i=\texttt{R}\),则面向顺时针方向。......
  • 字节跳动青训营 X 豆包MarsCode入营考核部分题解
    中等:观光景点组合得分问题小R正在研究一组观光景点,每个景点都有一个评分,保存在数组 values 中,其中 values[i] 表示第 i 个观光景点的评分。同时,景点之间的距离由它们的下标差 j-i 表示。一对景点 (i<j) 的观光组合得分为 values[i]+values[j]+i-j,也就......
  • ZZJC新生训练赛第十场题解
    先给出比赛链接:https://vjudge.net/contest/667035下面说一下难度分层:(同一难度下按字典序排序,数字为cf难度分)800:ABEG1100:D1200:C1400:H1500:F原题链接A:https://codeforces.com/contest/1850/problem/AB:https://codeforces.com/contest/1991/problem/......