Codeforces Round #430(Div.2) C,D,E题目详解
2017-09-08 18:58
281 查看
C题题意:询问从1(根节点)到其他所有节点的最大gcd是多少,你可以在该路径中将其中一个节点的值改为0或者一个都不改来影响最终的结果,所有节点的最大gcd分开考虑。
思路:一开始想到dp,但从他的根到孩子并没有什么联接性,满足根的gcd最大并不代表同样孩子的gcd最大。所以不能dp。
换种简单的思路,从上而下,将所有结果都保存下来,然后加上该点的值的影响,看最终结果哪个最大。一个简单的dfs即可。
D题题意:给你n个数(a1,a2,..an),每个询问,询问仅包含一个数x,将x异或这n个数,并且这n个数会被异或后的值代替。输出mex{a1,a2..an}.
思路:有个大佬说过,碰到mex就建一个trie,不知道这句话对不对,但这题确实要建。。试想我们要求一个不存在于异或后的a序列的最小值,那么很容易想到的就是将本来就不存在于a序列的值来异或这个值,然后输出一个最小的就行了。
需要特别说明的是,其实找异或后的最小的,并不需要真正的异或,从异或的性质我们可以看出,只要尽量和x的每一位相同,就能保证最小。另外,高位明显比地位影响更大,所以得先满足高位在满足地位。所以建树从高位开始建。
代码如下:
以为这样就完了吗?博主的博客并不简单,博主将要讲一个代码量极短但极难理解的想法。
刚刚的想法是反着推出答案的,那么我们能不能正着把答案做出来呢?
首先记住这个性质,建完一个字典树后,如果要求mex值,当一个结点的子树是满的,那么mex值肯定不在这个子树里面。
令a[i][j] 为第i层中前缀为j的数的个数。(字典树最下面层数为0)如果a[i][x^j]= ( 1 < < i)就说明这个的子树是满的。
沿着x从高位往低位走(道理同上,和x相同最好),如果走到第j层,它的子树是满的,那么ans最少也得是1<< j,所以ans|=(1<< j)。之后就得沿着它的兄弟树走了,重复上面的步骤。
代码如下:
E题题意:建一颗树,根为1,每升一级就加一个点,在每一级中,将x记为树上两点之间的最长距离,那么问在每一级中,有几个点到另外的点的距离可达到x。
思路:巧妙的想法,其实组成最长距离的点仅有两种深度(自己动手画画就明白了),那么可以用两个set来存两种深度的点,如果新加的点和其中一个set中的距离大于x,那么把另外的set清空,放入新加的点即可。
如果不会求树上的距离请查查lca。。
思路很简单,可是没做过差不多的就是想不出来深度竟然只有两种。。或许这就是套路吧。
代码如下:
终于把430的坑补完了。。
思路:一开始想到dp,但从他的根到孩子并没有什么联接性,满足根的gcd最大并不代表同样孩子的gcd最大。所以不能dp。
换种简单的思路,从上而下,将所有结果都保存下来,然后加上该点的值的影响,看最终结果哪个最大。一个简单的dfs即可。
#include<iostream> #include<vector> #include<set> #include<algorithm> using namespace std; #pragma warning (disable:4996) const int maxn = 200005; int a[maxn]; vector<int>V[maxn]; int ans[maxn]; int gcd(int a, int b) { return b == 0 ? a : gcd(b, a%b); } set<int>S[maxn]; void dfs(int u, int pre, int Gcd) { set<int>::iterator it; int temp,themax=-1; for (it = S[pre].begin();it != S[pre].end();it++) { temp = gcd(*it, a[u]); S[u].insert(temp); themax = max(themax, temp); } S[u].insert(Gcd); themax = max(themax, Gcd); Gcd = gcd(Gcd, a[u]); S[u].insert(Gcd); themax = max(themax, Gcd); ans[u] = themax; int Size = V[u].size(); for (int i = 0;i < Size;i++)if (V[u][i] != pre) { dfs(V[u][i], u, Gcd); } } int main() { int n; scanf("%d", &n); for (int i = 1;i <= n;i++) scanf("%d", &a[i]); int u, v; for (int i = 1;i < n;i++) { scanf("%d%d", &u, &v); V[u].push_back(v); V[v].push_back(u); } dfs(1, 0, 0); for (int i = 1;i <= n;i++) { if (i != 1)printf(" "); printf("%d", ans[i]); } cout << endl; return 0; }
D题题意:给你n个数(a1,a2,..an),每个询问,询问仅包含一个数x,将x异或这n个数,并且这n个数会被异或后的值代替。输出mex{a1,a2..an}.
思路:有个大佬说过,碰到mex就建一个trie,不知道这句话对不对,但这题确实要建。。试想我们要求一个不存在于异或后的a序列的最小值,那么很容易想到的就是将本来就不存在于a序列的值来异或这个值,然后输出一个最小的就行了。
需要特别说明的是,其实找异或后的最小的,并不需要真正的异或,从异或的性质我们可以看出,只要尽量和x的每一位相同,就能保证最小。另外,高位明显比地位影响更大,所以得先满足高位在满足地位。所以建树从高位开始建。
代码如下:
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; #define ll long long const int maxn = 300015; int a[maxn]; int node[600000 * 22][2]; int tot = 1; void build(int x) { int u = 0; for (int i = 20;i >= 0;i--) { int g = (x >> i) & 1; if (!node[u][g]) node[u][g] = tot++; u = node[u][g]; } } int query(int k) { int u = 0, ans = 0; for (int i = 20;i >= 0;i--) { int g = (k >> i) & 1; if (!node[u][g]) { g = 1 ^ g; } ans += (g << i); u = node[u][g]; } return ans; } bool vis[600000]; int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 0;i < n;i++) { scanf("%d", &a[i]); vis[a[i]] = 1; } //sort(a, a + n); //int j = 0; for (int i = 0;i <= 600000;i++)if (!vis[i]) build(i); int g = 0; while (m--) { int k; scanf("%d", &k); g = g^k; printf("%d\n", query(g) ^ g); } return 0; }
以为这样就完了吗?博主的博客并不简单,博主将要讲一个代码量极短但极难理解的想法。
刚刚的想法是反着推出答案的,那么我们能不能正着把答案做出来呢?
首先记住这个性质,建完一个字典树后,如果要求mex值,当一个结点的子树是满的,那么mex值肯定不在这个子树里面。
令a[i][j] 为第i层中前缀为j的数的个数。(字典树最下面层数为0)如果a[i][x^j]= ( 1 < < i)就说明这个的子树是满的。
沿着x从高位往低位走(道理同上,和x相同最好),如果走到第j层,它的子树是满的,那么ans最少也得是1<< j,所以ans|=(1<< j)。之后就得沿着它的兄弟树走了,重复上面的步骤。
代码如下:
#include<iostream> using namespace std; int a[20][1 << 19]; int main() { int n, m; cin >> n >> m; int x; for (int i = 1;i <= n;i++) { scanf("%d", &x); if (!a[0][x]) { for (int j = 0;j < 20;j++) { int temp = x >> j; a[j][temp]++; } } } int c = 0; for (int i = 1;i <= m;i++) { scanf("%d", &x); c ^= x; int ans = 0; for (int j = 19;j >= 0;j--) { if (a[j][(ans^c) >> j] == (1 << j)) ans |= (1 << j); } cout << ans << endl; } return 0; }
E题题意:建一颗树,根为1,每升一级就加一个点,在每一级中,将x记为树上两点之间的最长距离,那么问在每一级中,有几个点到另外的点的距离可达到x。
思路:巧妙的想法,其实组成最长距离的点仅有两种深度(自己动手画画就明白了),那么可以用两个set来存两种深度的点,如果新加的点和其中一个set中的距离大于x,那么把另外的set清空,放入新加的点即可。
如果不会求树上的距离请查查lca。。
思路很简单,可是没做过差不多的就是想不出来深度竟然只有两种。。或许这就是套路吧。
代码如下:
#include<iostream> #include<algorithm> #include<set> using namespace std; const int maxm = 300005; int fa[maxm][20]; int dep[maxm]; set<int>s1, s2; int getdist(int u, int v) { int res=0; if (dep[u] < dep[v])swap(u, v); for (int i = 19;i >= 0;i--) { if (dep[u] - dep[v] >= (1 << i)) { u = fa[u][i]; res += (1 << i); } } if (u == v)return res; for (int i = 19;i >= 0;i--) { if (fa[u][i] != fa[v][i]) { u = fa[u][i]; v = fa[v][i]; res += (1 << (i + 1)); } } return res + 2; } int main() { int m; cin >> m; int temp; s1.insert(1); int mx = 1; for (int i = 2;i <= m + 1;i++) { scanf("%d", &temp); fa[i][0] = temp; dep[i] = dep[temp] + 1; for (int j = 1;j < 20;j++) fa[i][j] = fa[fa[i][j - 1]][j - 1]; int dist1 = s1.empty() ? 0 : getdist(i, *s1.begin()); int dist2 = s2.empty() ? 0 : getdist(i, *s2.begin()); if (max(dist1, dist2) > mx) { mx = max(dist1, dist2); if (dist1 == mx) { for (int j : s2) { if (getdist(i, j) == mx) s1.insert(j); } s2.clear(); s2.insert(i); } else { for (int j : s1) { if (getdist(i, j) == mx) s2.insert(j); } s1.clear(); s1.insert(i); } } else if (max(dist1, dist2) == mx) { if (dist1 == mx)s2.insert(i); else s1.insert(i); } cout << s1.size() + s2.size() << endl; } return 0; }
终于把430的坑补完了。。
相关文章推荐
- Codeforces Round #430(Div.2)Problem C Ilya And The Tree(DFS)
- Codeforces Round #471 div.2 ABC题解
- Codeforces Round 261 Div.2 D Pashmak and Parmida&#39;s problem --树状数组
- Codeforces Round 389 Div.2 B. Santa Claus and Keyboard Check(模拟)
- Codeforces Round #430 A. Kirill And The Game
- Codeforces Round #430 (Div. 2) B. Gleb And Pizza(数论)
- Codeforces Round #430 (Div. 2) Vitya and Strange Lesson
- Codeforces Round #430 (Div. 2)C. Ilya And The Tree dfs D - Vitya and Strange Lesson
- Codeforces Round #430 (Div. 2) E. Nikita and game
- Codeforces Round #430 (Div. 2) C. Ilya And The Tree dfs+set
- Codeforces Round #430 (Div. 2) D.Vitya and Strange Lesson 异或 01字典树补集最小
- codeforces round #236 div.2 B
- Codeforces Round #FF (Div.2 )(C,D)
- CodeForces Round #290 Div.2
- Codeforces Round #427 (Div.2) A
- Codeforces Round 261 Div.2 E Pashmak and Graph --DAG上的DP
- Codeforces Round 389 Div.2 B. Santa Claus and Keyboard Check(模拟)
- codeforces Round #117 Div.2 182E. Wooden Fence
- Codeforces Round #430 C. Ilya And The Tree
- Codeforces Round #430 (Div. 2) D. Vitya and Strange Lesson