[LGR2021五月]题解
洛谷2021五月月赛题解
凑数
摸几下发现对于某一个 k,s 只要在一个区间内,他总是能被凑出来的,考虑这个区间左右端点,不过就是考虑最大值和最小值,可以想到最小值就是 [1,k] 的和,最大值就是 [n-k+1,n] 的和
Clear Up
还不会
猜树
有 1 为根节点,那么我们可以 O(n) 获得每一层的节点
然后考虑一层一层的搞,设相邻两层的节点数为 $n_i$ 和 $n_j$,我们有一个很显然的想法就是对于第 i 层的每一个节点,我们遍历所有的 j 层节点,并且用询问1搞出距离,这样距离为 1 的点就是他的儿子,复杂度 $O(n_in_j)$
可以发现这种做法可以被卡到 $O(n^2)$
考虑询问2来做这题
对于每一层的每一个节点,都询问他子树的节点。在这个点集里,位于下一层的节点就是他的儿子。那么复杂度是多少,我们可以发现在同一层里的询问是互不相交的,所以假设树有 k 层,总复杂度是 $O(kn)$
后者做法有一个好处,就是不管某一层有多少个点,他处理一层的复杂度总是 O(n)
那么两者结合一下,当用前者做法优于后者时,用前者做法,否则用后者做法,可以发现处理一层的复杂度是 $O(\min(n_in_j,n))$
那么总复杂度是多少?考虑最坏情况,每一层都是 $O(\sqrt n)$ 个节点,那么每一层都会被卡满到 O(n),但是我们这时只有 $O(\sqrt n)$ 层
所以总复杂度 $O(n\sqrt n)$
#include <ctime> #include <cmath> #include <cctype> #include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <vector> #include <queue> #define inf 5010 #define INF 0x7fffffff #define ll long long std::vector <int> depset[inf]; std::vector <int> q; int n; int maxdep; int fa[inf], dep[inf]; inline int querydist(int x, int y) { printf("? 1 %d %d\n", x, y); fflush(stdout); int t; std::cin >> t; return t; } inline int querytree(int x) { printf("? 2 %d\n", x); fflush(stdout); q.clear(); int sz, t; std::cin >> sz; for(int i = 1; i <= sz; i++) { std::cin >> t; q.push_back(t); } return sz; } inline void distfind(int d) { for(auto i: depset[d]) { for(auto j: depset[d + 1]) { int p = querydist(i, j); if(p == 1) fa[j] = i; } } } inline void treefind(int d) { for(auto i: depset[d]) { querytree(i); for(auto j: q) { if(dep[j] == d + 1) fa[j] = i; } } } signed main(){ std::cin >> n; q.reserve(n); for(int i = 2; i <= n; i++) { int d = querydist(1, i); depset[d].push_back(i); dep[i] = d; maxdep = std::max (maxdep, d); } for(int i = 1; i < maxdep; i++) { if((int)(depset[i].size() * depset[i + 1].size()) < n) distfind(i); else treefind(i); } for(auto i: depset[1]) fa[i] = 1; printf("! "); for(int i = 2; i <= n; i++) { printf("%d%c", fa[i], " \n"[i == n]); } fflush(stdout); return 0; }

浙公网安备 33010602011771号