NOIP 备赛:CF 2E 板刷

2024.11.05 之前的比赛排着刷。

CF2028 E

这道题主要考察的是手玩能力和转移技巧。
给定一棵树,根为 1。爱丽丝的起点位于某个顶点 v 。她想走出洞口,但不幸的是,红心皇后已经下令处死她。

每分钟都会掷一枚公平的硬币。如果硬币是正面,爱丽丝就可以移动到她当前位置的相邻顶点,反之,红心皇后就可以把爱丽丝拉到皇后选择的相邻顶点。如果爱丽丝最终出现在树的非根叶子上,那么爱丽丝就输了。

假设两人都以最佳方式移动,计算爱丽丝成功逃离每个起始顶点 1vn 的概率。对 998244353 取模的值。n105


首先需要一个 key observation,就是 Alice 的最优策略一定是向父亲走,红心皇后的最优策略一定是向深度最小的叶子走。设每个点深度最小的儿子是 su,Alice 从这个点出发逃离概率是 fu。那么有转移:

f(u)=12(f(su)+f(fau))

其中 f(1)=1,f(uuleaf)=0

这个转移方式有点技巧。假设有链 1234。那么 f(2)=12f(1)+12f(3)

接下来有 f(3)=12f(2)+12f(4)=14f(1)+14f(3)+12f(4)。移项整理得到 f(3)=13f(1)+23f(4)

同样操作可以发现规律 f(i)=i1f(1)+i1if(i+1)

首先将树按照浅儿子剖成若干链,对链单独转移,然后再对链顶单独转移即可。预处理逆元可以做到线性。

int f[N], n, m, fa[N], dep[N], s[N]; 
vector<int> E[N];
void dfs(int u, int F) {
	fa[u] = F; for (auto v : E[u]) if (v ^ F) {
		dfs(v, u); if (!dep[u]) dep[u] = dep[v] + 1, s[u] = v;
		else if (dep[v] + 1 < dep[u]) s[u] = v, dep[u] = dep[v] + 1;
	}
}
void dp(int u, int t, int d) {
	if (!s[u]) return; dp(s[u], t, d + 1); 
	f[u] = (d * qpow(d + 1) % mod * f[s[u]] % mod + qpow(d + 1) * t % mod) % mod;
	for (auto v : E[u]) if ((v ^ fa[u]) and (v ^ s[u])) dp(v, f[u] % mod, 1);
}
void sub() {
	read(n);
	rep(i, 1, n) E[i].clear(), dep[i] = 0, s[i] = 0, f[i] = 0;
	rop(i, 1, n) {
		int a, b; read(a, b);
		E[a].push_back(b);
		E[b].push_back(a);
	} dfs(1, 0); f[1] = 1; dp(1, 1, 0);
	rep(i, 1, n) printf("%lld ", f[i]);
	return;
}

CF2032 E

这道题考察的主要是观察力和构造水平。

给你一个数组 a,可以进行若干次操作,可以进行任意次操作。

每次操作选择一个位置 i(1in),并且使 aiai+2ai1ai1+1ai+1ai+1+1。特别地,1 的上一个位置是 nn 的下一个位置是 1,也就是说,这是一个循环数组。你需要构造一组方案。


首先我们发现这个答案有一定的单调性。即如果能使他们都变成 x,那么也可以通过把他们全操作一遍使他们变成 x+4。因此先设他们能变成一个大数 I,将问题转化为判定性问题。

不妨设 bi=Iai,则条件充要转化为:inpi1+2pi+pi+1=bi

发现将每个位置作为开头,剩下的往后顺延,将奇数位置的 b 的和,减去偶数位置的 b 的和,得到的就是 (pi+pi1)×2。有了这个之后,剩下的就好求了。复杂度是线性的。

void sub() {
	read(n);
	rep(i, 1, n) read(a[i]);
	auto solve = [&](int q) -> bool {
		rep(i, 1, n) b[i] = q - a[i];
		s[1] = b[1], s[2] = b[2];
		rep(i, 3, n) s[i] = s[i - 2] + b[i];
		rep(i, 1, n) {
			t[i] = s[(i & 1) ? n : n - 1] - s[max(i - 2, 0)];
			t[i] += s[i - 1];
			t[i] -= s[(i & 1) ? n - 1 : n] - s[max(i - 1, 0)];
			t[i] -= s[max(i - 2, 0)];
		}
		rep(i, 1, n) if (t[i] & 1) return 0; rep(i, 1, n) t[i] >>= 1;
		int o = 0; rep(i, 2, n) o += ((i & 1) ? -1 : 1) * t[i]; // p[1] - p[n]
		if ((o + t[1]) & 1) return 0;
		p[1] = (o + t[1]) >> 1;
		rep(i, 2, n) p[i] = t[i] - p[i - 1];
		rep(i, 1, n) if (p[i] < 0) return 0;
		rep(i, 1, n) printf("%lld ", p[i]); puts("");
		return 1;
	};
	if (solve(I)) return;
	if (solve(I + 1)) return;
	if (solve(I + 2)) return;
	if (solve(I + 3)) return;
}
posted @   Link-Cut-Y  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示