RiOI Round 1 Div.2

P8942 Digital Fortress

思路

  1. 观察样例的前缀异或和 { 1 , 7 , 15 , 31 } \{1,7,15,31\} {1,7,15,31},发现是 2 1 − 1 , 2 3 − 1 , 2 4 − 1 , 2 5 − 1 2^1-1,2^3-1,2^4-1,2^5-1 211,231,241,251。去反推前缀异或和为 2 1 − 1 , 2 2 − 1 , 2 3 − 1 , 2 4 − 1 2^1-1,2^2-1,2^3-1,2^4-1 211,221,231,241 时的原数集合,为 2 0 , 2 1 , 2 2 , 2 3 2^0,2^1,2^2,2^3 20,21,22,23,发现其后缀异或和同时不减。推测 2 0 , 2 1 , … 2 n − 1 2^0,2^1,\dots 2^{n-1} 20,21,2n1 为字典序最小(即最容易满足条件)的符合要求的集合。
  2. 证明 2 0 , 2 1 , … 2 n − 1 2^0,2^1,\dots 2^{n-1} 20,21,2n1 为字典序最小的集合,数学归纳法。当 n = 2 n=2 n=2 时, 1 , 2 1,2 1,2 显然符合前缀异或和的要求。假设当 n = k − 1 n=k-1 n=k1 时, 2 0 , 2 1 , … 2 k − 2 2^0,2^1,\dots 2^{k-2} 20,21,2k2 符合前缀异或和的要求。当 n = k n=k n=k 时, 2 0 xor ⁡ 2 1 … 2 k − 2 xor ⁡ 2 k − 1 = ( 11 … 11 ) 2 2^0\operatorname{xor}2^1\dots2^{k-2}\operatorname{xor}2^{k-1}=(11\dots11)_2 20xor212k2xor2k1=(1111)2。而对 ∀ a k ∈ [ 2 k − 2 , 2 k − 1 ) \forall a_{k}\in[2^{k-2},2^{k-1}) ak[2k2,2k1),都会使得 ( 11 … 11 ) 2 (11\dots11)_2 (1111)2 中除了最后一个 1 1 1 变为 0 0 0 外,前面的 1 1 1 中总会有若干个变为 0 0 0,此时 2 0 xor ⁡ 2 1 … 2 k − 2 > 2 0 xor ⁡ 2 1 … 2 k − 2 xor ⁡ a k 2^0\operatorname{xor}2^1\dots2^{k-2}>2^0\operatorname{xor}2^1\dots2^{k-2}\operatorname{xor}a_k 20xor212k2>20xor212k2xorak。故 2 0 , 2 1 , … , 2 k − 1 2^0,2^1,\dots,2^{k-1} 20,21,,2k1 符合前缀异或和的要求。后缀异或和符合要求同理可证。

代码

#include <bits/stdc++.h>
using namespace std;
long long bt[100];
int t, n;
long long m; 
int main ()
{
	bt[0] = 1;
	for (int i = 1; i <= 62; i++) 
		bt[i] = bt[i - 1] * 2;
	scanf ("%d", &t);
	while (t --) 
	{
		scanf ("%d %lld", &n, &m);
		if (n - 1 >= 63) 
		{
			printf ("No\n");
			continue;
		}
		if (bt[n - 1] > m) 
		{
			printf ("No\n");
			continue;
		}
		printf ("Yes\n");
		for (int i = 0; i <= n - 1; i++)
			printf ("%lld ", bt[i]);
		printf ("\n");
	}
	return 0;
}

P8943 Deception Point

思路 1 1 1 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 赛时思路

  1. 考虑这个图的构造,发现可以看作是一个环与其的多颗子树。易得,若 A 和 B 同时在环上的不同点,则答案便是 “Survive”。于是,问题即求在 A 上环前或恰恰上环时,B 能否赶到 A 的上环点。于是我们需要统计环上各点之间的距离以及子树上的点到其上环点的距离
  2. 考虑如何找到环。发现这个图的构造也可以看作是一棵树上的非父子关系的两点之间多连了一条边。于是,可以用并查集先求出其生成树以找到这条多的边。然后,对这条边的两个端点找其在生成树上的最近公共最先,但不倍增,就找出来了环。
  3. 考虑如何寻找并记录两点之间的距离。发现 n ≤ 2 × 1 0 5 n \le 2\times10^5 n2×105,故两点之间距离可以用 map 存。对于子树上的点到其上环点的距离,暴力搜索即可。对于环上两点间的距离,可以用相对的思想,即用 d i s u dis_u disu 表示环上一点 u u u 到环上另一固定点 s t a r t p o i n t startpoint startpoint 的距离,那么环上两点 u u u v v v 间的距离便是 min ⁡ ( ∣ d i s u − d i s v ∣ , t o t − ∣ d i s u − d i s v ∣ ) \min (|dis_u-dis_v|,tot-|dis_u-dis_v|) min(disudisv,totdisudisv) t o t tot tot 为环上的点的总个数。
  4. 时间复杂度为 O ( ( n + q ) log ⁡ n ) \mathcal O((n+q)\log n) O((n+q)logn)

代码 1 1 1

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
int n, q;
int head[maxn], cnt, cnti, cntii, headii[maxn];
struct edgei 
{
	int u, v;
} ei[maxn << 1];
struct edge 
{
	int v, next;
} e[maxn << 1], eii[maxn << 1];
void add (int tp, int u, int v) 
{
	if (tp == 1) 
		ei[++ cnti] = (edgei) {u, v};
	else if (tp == 0)
		e[++ cnt] = (edge) {v, head[u]},
		head[u] = cnt;
	else 
	{
		eii[++ cntii] = (edge) {v, headii[u]},
		headii[u] = cntii;
	}
}
map <pair <int, int>, int> dis;
void addi (int x, int y, int w) 
{
	dis.insert (make_pair (make_pair (x, y), w));
}
int fquery (int x, int y) 
{
	return (dis.find (make_pair (x, y)) -> second);
}
int f[maxn], vis[maxn], depth[maxn], fi[maxn];
int find (int x) 
{
	while (x != f[x]) x = f[x] = f[f[x]];
	return x;
}
void dfs (int u, int fa, int d) 
{
	depth[u] = d, fi[u] = fa;
	for (int i = head[u]; i; i = e[i].next) 
	{
		int v = e[i].v;
		if (v == fa) continue;
		dfs (v, u, d + 1);
	}
}
int zl[maxn], zp, rl[maxn], rp;
void find_circle (int x, int y) 
{
	if (depth[x] < depth[y]) swap (x, y);
	while (depth[x] > depth[y]) 
	{
		zl[++ zp] = x;
		x = fi[x];
	}
	if (x == y) 
	{
		zl[++ zp] = x;
		return;
	}
	while (fi[x] != fi[y]) 
	{
		zl[++ zp] = x, rl[++ rp] = y;
		x = fi[x], y = fi[y];
	} 
	zl[++ zp] = x, rl[++ rp] = y;
	zl[++ zp] = fi[x];
	return;
}
int wl[maxn], visi[maxn], p;
void Kruskal () 
{
	for (int i = 1; i <= n; i++)
		f[i] = i;
	for (int i = 1; i <= n; i++) 
	{
		int fx = find (ei[i].u), 
			fy = find (ei[i].v);
		if (fx == fy) continue;
		f[fy] = fx, vis[i] = 1;
		add (0, ei[i].u, ei[i].v);
		add (0, ei[i].v, ei[i].u);
	}
	dfs (1, 0, 1);
	for (int i = 1; i <= n; i++)
	{
		if (!vis[i]) 
		{
			find_circle (ei[i].u, ei[i].v);
			for (int j = 1; j <= zp; j++) 
			{
				wl[++ p] = zl[j];
				visi[zl[j]] = 1;
				addi (wl[p], wl[1], p - 1);
			}
			for (int j = rp; j >= 1; j--) 
			{
				wl[++ p] = rl[j];
				visi[rl[j]] = 1;
				addi (wl[p], wl[1], p - 1);
			}
			break;
		}
	}
}
int fii[maxn];
void dfsi (int u, int fa, int fai, int d)
{
	fii[u] = fai;
	addi (u, fai, d);
	for (int i = headii[u]; i; i = eii[i].next) 
	{
		int v = eii[i].v;
		if (v == fa || visi[v]) continue;
		dfsi (v, u, fai, d + 1);
	}
}
int main ()
{
	scanf ("%d %d", &n, &q);
	for (int i = 1; i <= n; i++)
	{
		int u, v;
		scanf ("%d %d", &u, &v);
		add (1, u, v);
		add (2, u, v), add (2, v, u);
	}
	Kruskal ();
	for (int i = 1; i <= n; i++) 
		if (visi[i]) dfsi (i, i, i, 0);
	while (q --) 
	{
		int a, b;
		scanf ("%d %d", &a, &b);
		int di = fquery (b, fii[b]),
			dii = fquery (a, fii[a]),
			diii = fquery (fii[a], wl[1]),
			div = fquery (fii[b], wl[1]);
		int dv = min (abs (diii - div), p - abs (diii - div));
		if (di + dv > dii) printf ("Survive\n");
		else printf ("Deception\n"); 
	}
	return 0;
}

思路 2 2 2 O ( n ) \mathcal O(n) O(n) 正解

  1. 赛后知道了这种图叫做基环树( n n n 个点, n n n 条边的联通图),可以实现 O ( n ) \mathcal O(n) O(n) 找环。
  2. 发现不用 map 便可以存储距离。我们可以用 d i s i dis_i disi 表示某一子树上的 i i i 到其上环点的距离,用 f i i fi_i fii 表示 i i i 的上环点,用 d i s i i disi_i disii 表示环上的一点 i i i 到环上另一固定点 s t a r t p o i n t startpoint startpoint 的距离,便实现了 O ( 1 ) \mathcal O(1) O(1) 查询。
  3. 时间复杂度为 O ( n + q ) \mathcal O(n+q) O(n+q)

代码 2 2 2

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
int head[maxn], cnt;
struct edge 
{
	int v, next;
} e[maxn << 1];
void add (int u, int v) 
{
	e[++ cnt] = (edge) {v, head[u]};
	head[u] = cnt;
} 
int n, q;
int dfn[maxn], vis[maxn], p, f[maxn], tot;
// 找环。
void getloop (int u, int fa) 
{
	dfn[u] = ++ p;
	for (int i = head[u]; i; i = e[i].next) 
	{
		int v = e[i].v;
		if (v == fa) continue;
		if (dfn[v]) 
		{
			if (dfn[v] < dfn[u]) continue;
			for (int x = v; x != f[u]; x = f[x])
				vis[x] = 1, tot ++;
		}
		else f[v] = u, getloop (v, u);
	}
}
// 得环上一点到固定点的距离。
int dis[maxn], start_point, disi[maxn];
void dfs (int u, int fa, int d) 
{
	disi[u] = d;
	for (int i = head[u]; i; i = e[i].next) 
	{
		int v = e[i].v;
		if (v == fa || !vis[v] || v == start_point) continue;
		dfs (v, u, d + 1);
		if (u == start_point) return; // 避免在把环倒着搜一遍。
	}
}
// 找子树上的点的上环点和到上换点的距离。
int fi[maxn]; 
void dfsi (int u, int fa, int fai, int d) 
{
	fi[u] = fai, dis[u] = d;
	for (int i = head[u]; i; i = e[i].next) 
	{
		int v = e[i].v;
		if (v == fa || vis[v]) continue;
		dfsi (v, u, fai, d + 1); 
	}
}
int main ()
{
	scanf ("%d %d", &n, &q);
	for (int i = 1; i <= n; i++) 
	{
		int u, v;
		scanf ("%d %d", &u, &v);
		add (u, v), add (v, u); 
	}
	getloop (1, 0);
	for (int i = 1; i <= n; i++) 
	{ 
		if (!vis[i]) continue;
		if (!start_point)start_point = i, dfs (i, f[i], 0);	
		dfsi (i, i, i, 0); 
	}
	while (q --) 
	{
		int a, b;
		scanf ("%d %d", &a, &b);
		int di, dii, diii;
		di = dis[b], dii = dis[a];
		// 相对得环上两点之间得距离。
		diii = abs (disi[fi[a]] - disi[fi[b]]);
		diii = min (diii, tot - diii);
		// 赶不到则是 A 胜利。
		if (di + diii > dii) printf ("Survive\n");
		else printf ("Deception\n");
	}
	return 0;
}

收获

  1. 见识到了基环数,学会了 O ( n ) \mathcal O(n) O(n) 地找环。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,根据提供的引用内容,我无法理解你具体想要问什么问题。请提供更清晰明确的问题,我将竭诚为你解答。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【Codeforces】 Codeforces Round 865 (Div. 2) (补赛)](https://blog.csdn.net/t_mod/article/details/130104033)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值