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

P6803 [CEOI2020] 星际迷航 题解

时间:2024-12-17 19:54:02浏览次数:3  
标签:Mar matrix int 题解 迷航 CEOI2020 必败 dth dtw

\(\text{P6803 [CEOI2020] 星际迷航 题解}\)

观察这个从第 \(0\) 棵树走向第 \(D\) 棵树的过程是困难的,我们难以判定走到下一棵树的情况。于是我们不妨从第 \(D\) 棵树向第 \(0\) 棵树来倒推。从第 \(i\) 层走向第 \(i+1\) 层的边为 \(x\to y\) 时,事实上此时 \(y\) 就是第 \(i+1\) 棵子树的根。当我们在处理连边 \(x\to y\) 时,也就是在为 \(y\) 寻找一个合适的 \(x\) 的过程。于是我们设 \(f(i,0/1)\) 表示考虑了 \(i\) 层树,从第 \(1\) 棵树的某个节点开始走,此时根节点先手必败/必胜的方案数。

根据我们刚才的分析,现在需要考虑的是在第 \(1\) 棵树前面加上第 \(0\) 棵树的情形。记这条边为 \(x\to y\)。设此时第 \(0\) 棵树的根节点为 \(p\)。考虑当 \(p\) 先手必胜,即 \(f(i+1,1)\) 时:

  • 若 \(p\) 原本先手必胜,则添加的 \(x\to y\) 不能改变 \(p\) 现在的状态;
  • 否则必须改变 \(p\) 的状态。

\(p\) 先手必败时是同理的。于是我们分析 \(x\to y\) 改变 \(p\) 状态的条件。若 \(x\) 原先是必胜的,那么无论如何也不会改变 \(x\) 的状态,\(x\) 原先必败时,也只有 \(y\) 同样为必败才能改变 \(p\) 的状态。那么此时 \(y\) 的状态包含在 \(f\) 中,我们需要求的是向必败点连边后可以改变根节点个数的点。我们设它为 \(g(x)\),同时设 \(t(x)\) 表示以 \(x\) 为根使先手必败/必胜。

那么 \(f\) 的转移式是容易的:

\[f(i+1,0)\longleftarrow\sum_{x=1}^{n}\left\{\begin{matrix} g(x)f(i,0), &t(x)=0 \\ n(f(i,0)+f(i,1))-g(x)f(i,0), &t(x)=1 \end{matrix}\right. \]

\[f(i+1,1)\longleftarrow\sum_{x=1}^{n}\left\{\begin{matrix} g(x)f(i,0), &t(x)=1 \\ n(f(i,0)+f(i,1))-g(x)f(i,0), &t(x)=0 \end{matrix}\right. \]

现在的目标是求出 \(t\) 和 \(g\)。对于容易一些的 \(t\),我们设 \(h(x)\) 表示 \(x\) 儿子中必败点的个数,那么 \(h\) 的转移是显然的,\(t\) 的转移也容易得到。现在的问题是求出 \(g\)。那这个东西还是要按 \(h\) 的取值分类讨论。当 \(h(x)>1\) 时,显然 \(g(x)=0\),\(h(x)=1\) 时,记必败的儿子为 \(y\),那么 \(g(x)=g(y)\),\(h(x)=0\) 时,\(g(x)\) 就是 \(\sum g(\operatorname{son_x})+1\),加上的 \(1\) 是 \(x\) 本身。

为了方便一些,可以记 \(w(x,0/1)\) 表示以 \(x\) 为根时,\(x\) 儿子里必胜/必败的点的 \(g\) 的和。这里给出 \(g\) 的形式化转移式:

\[g(x)=\left\{\begin{matrix} w(x,1)+1, &h(x)=0 \\ w(x,0), &h(x)=1 \\ 0,&\text{Other.} \end{matrix}\right.\\ \]

那么我们知道了 \(t(1),g(1)\) 后做一个换根就可以求出所有的 \(t,g\) 值了。对于 \(f\) 的转移,用矩阵是好维护的。

代码:

#include <bits/stdc++.h>
#define N 100005
#define int long long
#define mod 1000000007
using namespace std;
int n, d;
vector<int>v[N];
int h[N];
int w[N][2];
int g[N];

void dfs1(int x, int fa) {
	for (auto y : v[x]) {
		if (y == fa) continue;
		dfs1(y, x);
		h[x] += !h[y];
		w[x][h[y] > 0] += g[y];
	}
	if (h[x] == 0) g[x] = w[x][1] + 1;
	else if(h[x] == 1) g[x] = w[x][0];
	else g[x] = 0;
}

int ct;

int dth, dtg, dtw[2];
void dfs2(int x, int fa) {
	if (h[x] == 0) ++ct;
	for (auto y : v[x]) {
		if (y == fa) continue;
		dtw[0] = w[x][0], dtw[1] = w[x][1];
		dth = h[x] - !h[y];
		dtw[h[y] > 0] -= g[y];
		if (dth == 0) dtg = dtw[1] + 1;
		else if(dth == 1) dtg = dtw[0];
		else dtg = 0;
		h[y] += !dth;
		w[y][dth > 0] += dtg;
		if (h[y] == 0) g[y] = w[y][1] + 1;
		else if(h[y] == 1) g[y] = w[y][0];
		else g[y] = 0;
		dfs2(y, x);
	}
}

struct Mar {
	int a[2][2];
	Mar() {
		memset(a, 0, sizeof a);
	}
};
void add(int &x, int y) {
	x = ((x + y) % mod + mod) % mod;
}
Mar operator * (Mar &a, Mar &b) {
	Mar res;
	for (int i = 0; i < 2; i++)
		for (int j = 0; j < 2; j++)
			for (int k = 0; k < 2; k++)
				add(res.a[i][j], a.a[i][k] * b.a[k][j] % mod);
	return res;
}
Mar qpow(Mar x, int y) {
	Mar ans;
	ans.a[0][0] = ans.a[1][1] = 1;
	while (y) {
		if (y & 1) ans = ans * x;
		x = x * x;
		y >>= 1;
	}
	return ans;
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> n >> d;
	for (int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		v[x].push_back(y);
		v[y].push_back(x);
	}
	dfs1(1, 0);
	dfs2(1, 0);
	Mar res;
	for (int i = 1; i <= n; i++) {
		if (h[i]) {
			add(res.a[0][0], g[i]);
			add(res.a[0][1], n - g[i]);
			add(res.a[1][1], n);
		}
		else {
			add(res.a[0][0], n - g[i]);
			add(res.a[0][1], g[i]);
			add(res.a[1][0], n);
		}
	}
	res = qpow(res, d - 1);
	Mar ans;
	ans.a[0][0] = ct, ans.a[0][1] = n - ct;
	ans = ans * res;
	if (h[1]) cout << (n * (ans.a[0][0] + ans.a[0][1]) % mod - g[1] * ans.a[0][0] % mod + mod) % mod << "\n";
	else cout << g[1] * ans.a[0][0] % mod << "\n";
	return 0;
}

标签:Mar,matrix,int,题解,迷航,CEOI2020,必败,dth,dtw
From: https://www.cnblogs.com/Rock-N-Roll/p/18613310

相关文章

  • CF335F 题解
    \(CF335F\)\(Buy\)\(One\),\(Get\)\(One\)\(Free\)考虑可撤销贪心+小根堆维护。首先价格相同的馅饼可以放到一起考虑,从大到小排序后考虑每种不同价格的馅饼。则第\(i\)种最多白嫖的个数为\(p=\min(c_i,num-2*sum)\),其中\(c_i\)为馅饼个数,\(num\)为已经考虑的更贵的......
  • 7 Oracle 经典面试题解析
    PL/SQL面试题--1、创建序列seq_employee,该序列每次取的时候自动增加,从1开始计数,不设最大值,并且一直累加,不循环;CREATESEQUENCEseq_employeeSTARTWITH1INCREMENTBY1NOMAXVALUE;--也可以直接使用简单默认参数:--CREATESEQUENCEseq_employee;SELECTseq_employee.NEXTV......
  • 题解:牛客周赛 Round 72(A-D)(E只有代码)
    先附上补题链接,没打的同学可以来补一下:https://ac.nowcoder.com/acm/contest/98256A小红的01串(一)题意找到一个01串中相邻字符不同的对数做法从头到尾扫一遍,计算前后不一样的字符就可以了#include<bits/stdc++.h>signedmain(){std::ios::sync_with_stdio(false)......
  • 题解:AT_abc266_c [ABC266C] Convex Quadrilateral
    思路对于一个凸多边形,它的任意内角一定小于\(45\degree\)。如果每相邻两条边的叉积的符号相同就说明它们是顺时针或逆时针排列的,则可以判别出该四边形是否为凸四边形。AC代码#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;intX1,X2,X3,X4,Y1,Y2,Y3......
  • 题解:AT_abc296_e [ABC296E] Transition Game
    题目传送门思路我们可以在环中任选一点,然后在环内可以转到另一个点。因为起点自由选择,所以环中每个点都可以到达,由此我们可以得知环上的所有点都是必胜点。我们把这个问题抽象为一张图,用拓扑排序判环即可。AC代码#include<bits/stdc++.h>usingnamespacestd;usingll=l......
  • 题解:B3832 [NICA #2] 回来吧我的小波
    思路经典抽屉原理。对于长度大于\(9\)的子串,我们就可以认为它一定是好的,因为一定有两个数是相同的,它们可以互相整除。对于剩下长度小于等于\(9\)的子串,我们对它们进行暴力枚举即可。AC代码#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;strings......
  • 题解:B3803 [NICA #1] 上大分
    思路看到这道题首先考虑贪心和动态规划。贪心是不行的,因为这里有先减分再加分的数据,也就是说故意在div1的比赛掉分,使得下一次能够打div2加更多的分。我们考虑动态规划,我们用\(f[i][j]\)表示在前\(i\)场比赛中得\(j\)分至少需要打几场比赛,就可以轻易推出这题的转移方......
  • 题解:AT_abc236_f [ABC236F] Spices
    今天2024秋令营Day1的贪心例题,来解释一下这道题贪心的思路。首先输入一个整数\(n\),表示需要处理的数字数量为\(2^n-1\),随后输入每个编号的代价,并将代价和编号存储在数组\(a\)中。接着,对代价进行排序,以便在后续处理中优先选择代价较小的数字。然后,使用一个\(vis\)数......
  • 题解:P7020 [NWRRC2017] Boolean Satisfiability
    首先,我们需要明确一个重要的恒等式:\[x\mid\nega=1\]当\(x=1\)时,\(x\mid\negx=1\mid0\)的结果为\(1\)。当\(x=0\)时,\(x\mid\negx=0\mid1\)的结果同样为\(1\)。因此,我们可以得出结论,该式子的结果恒为\(1\)。接下来,我们观察到,当表达式中仅包含......
  • 牛客周赛 Round 72 题解
    牛客周赛Round72题解A小红的01串(一)直接遍历即可#include<bits/stdc++.h>usingnamespacestd;voidsolve(){ strings;cin>>s;intn=s.size();intcnt=0;for(inti=1;i<n;i++){if(s[i]!=s[i-1])cnt++;}......