概述
P8942 Digital Fortress
思路
- 观察样例的前缀异或和 { 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 21−1,23−1,24−1,25−1。去反推前缀异或和为 2 1 − 1 , 2 2 − 1 , 2 3 − 1 , 2 4 − 1 2^1-1,2^2-1,2^3-1,2^4-1 21−1,22−1,23−1,24−1 时的原数集合,为 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,…2n−1 为字典序最小(即最容易满足条件)的符合要求的集合。
- 证明 2 0 , 2 1 , … 2 n − 1 2^0,2^1,dots 2^{n-1} 20,21,…2n−1 为字典序最小的集合,数学归纳法。当 n = 2 n=2 n=2 时, 1 , 2 1,2 1,2 显然符合前缀异或和的要求。假设当 n = k − 1 n=k-1 n=k−1 时, 2 0 , 2 1 , … 2 k − 2 2^0,2^1,dots 2^{k-2} 20,21,…2k−2 符合前缀异或和的要求。当 n = k n=k n=k 时, 2 0 xor 2 1 … 2 k − 2 xor 2 k − 1 = ( 11 … 11 ) 2 2^0operatorname{xor}2^1dots2^{k-2}operatorname{xor}2^{k-1}=(11dots11)_2 20xor21…2k−2xor2k−1=(11…11)2。而对 ∀ a k ∈ [ 2 k − 2 , 2 k − 1 ) forall a_{k}in[2^{k-2},2^{k-1}) ∀ak∈[2k−2,2k−1),都会使得 ( 11 … 11 ) 2 (11dots11)_2 (11…11)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^0operatorname{xor}2^1dots2^{k-2}>2^0operatorname{xor}2^1dots2^{k-2}operatorname{xor}a_k 20xor21…2k−2>20xor21…2k−2xorak。故 2 0 , 2 1 , … , 2 k − 1 2^0,2^1,dots,2^{k-1} 20,21,…,2k−1 符合前缀异或和的要求。后缀异或和符合要求同理可证。
代码
#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 ("Non");
continue;
}
if (bt[n - 1] > m)
{
printf ("Non");
continue;
}
printf ("Yesn");
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(nlog n) O(nlogn) 赛时思路
- 考虑这个图的构造,发现可以看作是一个环与其的多颗子树。易得,若 A 和 B 同时在环上的不同点,则答案便是 “Survive”。于是,问题即求在 A 上环前或恰恰上环时,B 能否赶到 A 的上环点。于是我们需要统计环上各点之间的距离以及子树上的点到其上环点的距离。
- 考虑如何找到环。发现这个图的构造也可以看作是一棵树上的非父子关系的两点之间多连了一条边。于是,可以用并查集先求出其生成树以找到这条多的边。然后,对这条边的两个端点找其在生成树上的最近公共最先,但不倍增,就找出来了环。
- 考虑如何寻找并记录两点之间的距离。发现 n ≤ 2 × 1 0 5 n le 2times10^5 n≤2×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(∣disu−disv∣,tot−∣disu−disv∣), t o t tot tot 为环上的点的总个数。
- 时间复杂度为 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 ("Surviven");
else printf ("Deceptionn");
}
return 0;
}
思路 2 2 2 O ( n ) mathcal O(n) O(n) 正解
- 赛后知道了这种图叫做基环树( n n n 个点, n n n 条边的联通图),可以实现 O ( n ) mathcal O(n) O(n) 找环。
- 发现不用 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) 查询。
- 时间复杂度为 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 ("Surviven");
else printf ("Deceptionn");
}
return 0;
}
收获
- 见识到了基环数,学会了 O ( n ) mathcal O(n) O(n) 地找环。
最后
以上就是外向网络为你收集整理的RiOI Round 1 Div.2的全部内容,希望文章能够帮你解决RiOI Round 1 Div.2所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复