现在要构造一颗树堆,它的每个节点定义是 (x, y = sin x);现在你希望它包含 n 个节点,且平衡性是所有 n-树堆 中最差的;你要构造出这 n 个节点的 x 值,且 x 的类型是 __int32。
数据范围:1 ≤ n ≤ 50000,-2³¹ ≤ x ≤ 2³¹ - 1;
首先,”最不平衡“指的是左右子树的高度差最大;显然,一颗 n 个节点构成的 Treap 能达到的最大的高度差是 n-1:即当整棵树退化成一条链的时候;那么在什么样的情况下,这棵树会退化成一条链呢?一种很简单的情况,就是当 K 和 V 值都是单调的时候,堆的性质一定会满足,所有节点都是其父节点的左二子或是右儿子;
观察节点的函数 (K: x, V: y = sin x):假设我们想要一个 V 随着 K 增大而增大的区间,那么这个区间可以是 x ∈ [-π/2, π/2];但是题目要求了键 K 是 __int32 类型,不然我们直接将这个区间平均分配就可以了;但是另注意到,sin x 是一个周期函数,所以对于同一个 V 值,可以对应多个差距为 2kπ 的 K 值,只要这些 K 值递增且为整数,就可以构造出一个整数数列;那么现在我们想要构造一组 K = -π/2 + 2πi / T + 2kπ,使得他们都是整数且数列递增;显然,增量 δ = 2π / T + εkπ;
template <typename number> classcomparable : binary_function<number, number, bool> { number eps = 1e-8; intcompareTo(number x)const{return x < -eps ? -1 : x > eps;}
public: explicitcomparable(number eps) : eps(eps){} intcompareTo(number a, number b)const{return compareTo(a-b);} intoperator()(number a, number b)const{return compareTo(a-b);} };
longdoublecalcDelta(longdouble precision, unsigned count) { staticconstlongdouble pi = 3.1415926535897932384626; staticconstlongdouble dpi = 2.0 * pi; comparable<longdouble> cmp(precision); constlongdouble delta = pi / count; cerr << fixed << setprecision(10) << "delta: " << delta << endl; longdouble xx = delta, ans = xx; int cnt = 0; while (cmp(ans, ceil(ans))) ++ cnt, ans = xx + cnt * dpi; // unsequenced modification and access to "ans" returncerr << "found: " << ans << endl, ans; }
因为题目的要求是 50000 数据范围,如果调用上述的 calcDelta(1e-5, 50000) 得到的是 1420,交上去会挂掉;因为 C++ 浮点数有着大家都知道的误差,所以一般来说 T 应该更大的数,比如 60000,求出的增量 710,就可以过了;此外按照 hjl 巨佬的 ppt 上的推法也可以得到相同的结果;最后代码就像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13
#include<iostream>
usingnamespacestd;
intmain() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin >> n; for (int i = 0; i < n; ++ i) cout << 710 * (i - 25000) << endl; return0; }
C. Cross-Stitch
Cross-Stitch [n] 十字绣给你一个 h 行 w 列的十字绣图案,要求使用一根线将它绣出来,并且消耗线的长度最少;输出这种绣法包含的针数(孔数),并且按照先后顺序输出线经过的点的坐标。
int m, n; cin >> m >> n; init(n, m); int sp, div = m + 1; for (int i = 1; i <= n; ++ i) cin >> (map[i] + 1); for (int i = 1; i <= n; ++ i) for (int j = 1; j <= m; ++ j) if (map[i][j] == 'X') sp = id[i][j], addEdge(id[i - 1][j - 1], id[i][j], 1), addEdge(id[i - 1][j], id[i][j - 1], 1), addEdge(id[i - 1][j], id[i][j], 0), addEdge(id[i - 1][j - 1], id[i][j - 1], 0); dfs(sp, 1); cout << (ans.size() - 1) << endl; for (auto & ii : ans) cout << ii % div << ' ' << ii / div << endl;
return0; }
D. Double Palindrome
定义双回文串:一个字符串是双回文串,当且仅当它是一个回文串或两个回文串的组合;现在询问长度不超过 n 的,由大小为 k 的字符集排列组合构成的所有字符串中回文串的数量,输出答案取模。
数据规模:1 ≤ k ≤ 26 并且 1 ≤ n ≤ 1e5。
占坑
E. Equidistant
有 n 个城市连成一棵树,每两个相邻的城市连接的边的长为 1;现在这些城市中的一部分(或者全部)都有人要参加区域赛,问比赛场地设置在哪座城市,才可以保证所有城市的参赛者到达比赛场所需要走的路径长度一致,不存在这样的城市时输出 Impossible。
没有什么特别优秀的做法,考虑暴力,但是并不是直接就暴力了——n²给卡到死;仔细思考一波,发现最多只要扫描所有的节点就一定可以找到这个点,那么就可以考虑多起点 BFS:每当一个节点到达一个节点的时候,如果深度和当前深度一致就增加一次访问次数,这样最终只需要遍历所有节点,找到是否存在被访问了 n 次的节点存在就可以了。
cin >> n >> m; int u, v; FWS::init(n); for (int i = 1; i < n; ++ i) { cin >> u >> v; FWS::addEdge(u, v, 1); } for (int i = 1; i <= m; ++ i) cin >> c[i]; int found = BFS(); if (found < 0) cout << "NO" << endl; elsecout << "YES" << endl << found << endl;
foreach ($a as &$x) if ($x == <some integer value>) break;
值版本:将变量(引用的元素)设置为一个数组元素的值;同样只能使用如下形式
1
foreach ($a as $x) if ($x == <some integer value>) break;
接下来题目会给你一个长度为 n 的数组 $a,并且指定目标状态;要求你生成将原数组转化为目标状态的代码(仅由上述两个语句构成),并在第一行输出代码行数;如果不可能生成这样的代码。输出 -1;
数据规模:数组长度 1 ≤ n ≤ 50,元素(包括原始状态和目标状态)的值 1 ≤ si, ti ≤ 100;
使用谷歌生草机来学习共产主义唯一指定语言——俄语
G. Golf Time
有一个宽为 w,高为 h 的球场,定义坐标系:+x 向右,+y 向上;球场内有一个 n 边形池塘,如果球到达了池塘位置就会立即下沉,输入将按照顺时针方向描述池塘边缘所有的格点;球总是朝东北方向发出,并保持匀速直线运动 (1, 1);当球撞到了边缘,球会被无能量损失的反弹(仅改变运动方向);现在告诉你 t 个发球点,问这个球是否可以永远的运动下去,或在落水之前运动的时间以及落水时的准确位置。
数据规模:4 ≤ w, h ≤ 5e8,4 ≤ n ≤ 1000,1 ≤ t ≤ 100;
占坑
H. High Load Database
有一个数据库,里面有 n 块数据,每一块数据的大小是 ai;接下来尝试将这个大数据库分库成多个单个存储了不超过 t 数据的数据库:要求数据块的顺序保持和原数据库一致,且每一块数据一定要放在一个数据库中;进行 q 次询问,每次询问 ti,要求求出分成的数据库的数量,或者不能够实现目标。
数据规模:1 ≤ n ≤ 2e6,1 ≤ Σai ≤ 1e6,1 ≤ q ≤ 1e6,1 ≤ t ≤ Σai;
不能分库的情况很显然,就是存在单块数据大于这个要求的数据库最大容量 t,做一下特殊处理就好了;接下来对于单次询问 t,也很容易有这样的思路:从第一块数据开始贪心的向右边数,当空间超过 t 的时候就取出最后一块数据,并且统计次数自增即可;这样对于单次询问是 O(n) 的。
while (++ cnt) { int tol = 0, bin = 1; while (bin) if (l + tol + bin > n || a[l + tol + bin] - a[l] > t) bin >>= 1; else tol += bin, bin <<= 1; if (l + tol == n) break; l += tol; }
int n, map[N][N], ans[N][N]; char ch; cin >> n; for (int i = 1; i <= n; ++ i) for (int j = 1; j <= n; ++ j) { cin >> ch; map[i][j] = ch - '0'; } for (int i = 1; i < n; ++ i) for (int j = i + 1; j <= n; ++ j) { ans[i][j] = map[i][j]; if (map[i][j]) for (int k = 1; k <= n; ++ k) { map[i][k] -= map[j][k]; while (map[i][k] < 0) map[i][k] += 10; } } for (int i = 1; i <= n; ++ i) { for (int j = 1; j <= n; ++ j) cout << ans[i][j]; cout << endl; }
return0; }
K. King’s Children
国王有一个 n × m 矩阵的地图,每一个方格是地盘的最小单位;王有不超过 26 个儿子,用字母表示;每一个儿子有一个城堡,位于地图中的某个方格上:这个方格使用这个字母的大写形式标识,其他方格用 . 表示;现在国王要将地盘分给儿子们,分给儿子们的地盘用字母的小写形式表示;分地图有要求:所有领土必须全部分给儿子,且每个儿子得到的领土是严格的网格矩阵;此外,国王希望 A 儿子分到的领土尽可能的大;现在要求你将分割后的矩阵计算出来并输出。
数据范围:1 ≤ n, m ≤ 1000
好麻烦的 DSU 题目…… 直接翻译题意就可以知道,首先我们要求出整个矩阵中 A 可以拓展的最大子矩阵,然后再拓展其他的字母,将整个矩阵填满;填充最大子矩阵可以使用很多种方法:比如悬线法,或者是单调数据结构等等,求出了 A 的最大子矩阵之后,就将矩阵剩余部分划分成了四个部分;因为其他的字母只需要扩充后填满整个矩形即可,所以不再需要跑一遍最大子矩阵了,随便使用什么乱搞的方法将矩阵填满就可以了。