#635 div2 A - F 题解
Posted nonameless
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了#635 div2 A - F 题解相关的知识,希望对你有一定的参考价值。
$Description:$
给你四个整数 $a, b, c, d$, 选择三个数$x, y, z$构成一个三角形,其中$aleq xleq b, bleq yleq c, cleq zleq d$.
打印任何一组符合条件的$x, y, z$即可。
$Solve:$
构成三角形则需要满足任意两边之和大于第三边,那么我们令$x = b, y = c, z = c$就一定符合条件。
$Code:$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 int t; cin >> t; 7 while(t --) 8 { 9 int a, b, c, d; 10 cin >> a >> b >> c >> d; 11 printf("%d %d %d ", b, c, c); 12 } 13 return 0; 14 }
$Description:$
怪兽的血量为$x$,你有两种攻击方式:
$1:x = left lfloor x / 2 ight floor + 10$
$2:x = x - 10$
第一种可以使用$n$次,第二种可以使用$m$次,请问你是否可以消灭怪兽$(x <= 0)$。
$Solve:$
直接使用$n$次第一种攻击来使怪兽血量大幅下降,然后判断使用$m$次第二种攻击是否可以消灭怪兽。
有一种特殊情况是初始值$x leq 10 $ $ And $ $ m = 1$,这种情况使用上面的方案就不能成功,但可以直接使用$m$次攻击,所以我们需要特判一下。
$Code:$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 int t; cin >> t; 7 while(t --) 8 { 9 int x, n, m; 10 cin >> x >> n >> m; 11 if(m * 10 >= x) { puts("Yes"); } 12 else 13 { 14 for(int i = 1; i <= n; i ++) 15 x = x / 2 + 10; 16 if(m * 10 >= x) puts("Yes"); 17 else puts("No"); 18 } 19 } 20 return 0; 21 }
$Description:$
给你一颗树,$1$为根,选择$k$个结点使其权值为$0$,其他结点的权值为$1$,这$k$个点都往根节点走,每经过一个点,加上他的权值,所获得的权值和为这个点的幸福度,最后求$k$个点的幸福度之和最大可以是多少?
$Solve:$
首先很容易知道的是,选择了一个结点$a$,那么以$a$为根的子树也一定选择了,否则我们就会浪费掉未选择的(因为$a$走不到那个结点,所以浪费了他的权值)。
于是我们可以计算出每个点的幸福度,然后排序,取前$k$大求和即是答案。
举例来计算某个点的幸福度:
计算$6$的权值,即我们选择了$6$号点,那么他的子树也一定选择了,由于在计算他的子节点时,$6$还没有被选择,所以他们加上了$6$的权值,即多加了$cnt[6](cnt[6]是6子节点个数)$,所以我们需要减去$cnt[6]$,再加上$d[6](d[6]是6到根节点的距离)$,因为此时$6$之上的结点还没有被选择,所以他们的权值都是$1$。
$Code:$
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 2e5 + 10, M = N << 1; 4 typedef long long ll; 5 6 int n, k; 7 int h[N], e[M], ne[M]; 8 int tot = 0; 9 10 11 ll d[N], cnt[N], val[N]; 12 13 void add(int a, int b) 14 { 15 e[ ++ tot] = b; ne[tot] = h[a]; h[a] = tot; 16 } 17 18 int dfs(int p, int fa, int st) 19 { 20 d[p] = st; 21 int res = 1; 22 for(int i = h[p]; i; i = ne[i]) 23 { 24 int j = e[i]; 25 if(j == fa) continue; 26 res += dfs(j, p, st + 1); 27 } 28 cnt[p] = res; 29 return res; 30 } 31 32 int main() 33 { 34 cin >> n >> k; 35 for(int i = 1; i < n; i ++) 36 { 37 int a, b; scanf("%d %d", &a, &b); 38 add(a, b); add(b, a); 39 } 40 dfs(1, -1, 0); 41 for(int i = 1; i <= n; i ++) val[i] = d[i] - cnt[i] + 1; 42 sort(val + 1, val + n + 1, greater<int>()); 43 ll ans = 0; 44 for(int i = 1; i <= k; i ++) ans += val[i]; 45 cout << ans << endl; 46 return 0; 47 }
$Description:$
你有三个正整数集合,从三个集合中分别选出一个数为$x, y, z$, 使得$(x - y)^2 + (x - z)^2 + (y - z) ^ 2$最小是多少。
$Solve:$
显然对于选出来的三个数一定存在以下大小关系的:
$xleq yleq z$, $xleq zleq y$,$yleq xleq z$,$yleq zleq x$,$zleq xleq y$,$zleq yleq x$
那么我们可以枚举中间的数,再用二分分别算出小于等于自己的最大数和大于等于自己的最小数,再利用公式得出结果,在所有的结果中取最小值即可。
$Code:$
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 10; 4 const long long INF = 3e18 + 10; 5 typedef long long ll; 6 7 ll r[N], g[N], b[N]; 8 ll ans = INF; 9 10 void read(int n, ll *a) 11 { 12 for(int i = 1; i <= n; i ++) 13 scanf("%lld", &a[i]); 14 } 15 16 ll cal(ll x, ll y, ll z) 17 { 18 return (x - y) * (x - y) + (x - z) * (x - z) + (y - z) * (y - z); 19 } 20 21 ll findLess(ll *a, int na, ll x) 22 { 23 int l = 1, r = na; 24 ll res = -1; 25 while(l <= r) 26 { 27 int m = l + r >> 1; 28 if(a[m] <= x) 29 { 30 l = m + 1; 31 res = a[m]; 32 } 33 else r = m - 1; 34 } 35 return res; 36 } 37 38 ll findMore(ll *a, int na, ll x) 39 { 40 int l = 1, r = na; 41 ll res = -1; 42 while(l <= r) 43 { 44 int m = l + r >> 1; 45 if(a[m] >= x) 46 { 47 r = m - 1; 48 res = a[m]; 49 } 50 else l = m + 1; 51 } 52 return res; 53 } 54 55 void solve(ll *a, ll *b, ll *c, int na, int nb, int nc) 56 { 57 for(int i = 1; i <= na; i ++) 58 { 59 ll x = a[i]; 60 ll y = findLess(b, nb, x); 61 ll z = findMore(c, nc, x); 62 if(y == -1 || z == -1) continue; 63 ans = min(ans, cal(x, y, z)); 64 } 65 } 66 67 int main() 68 { 69 int t; cin >> t; 70 while(t --) 71 { 72 ans = INF; 73 int nr, ng, nb; 74 scanf("%d%d%d", &nr, &ng, &nb); 75 read(nr, r); read(ng, g); read(nb, b); 76 sort(r + 1, r + nr + 1); 77 sort(g + 1, g + ng + 1); 78 sort(b + 1, b + nb + 1); 79 solve(r, b, g, nr, nb, ng); 80 solve(r, g, b, nr, ng, nb); 81 solve(b, r, g, nb, nr, ng); 82 solve(b, g, r, nb, ng, nr); 83 solve(g, r, b, ng, nr, nb); 84 solve(g, b, r, ng, nb, nr); 85 cout << ans << endl; 86 } 87 return 0; 88 }
$Description:$
给你两个字符串$S,T和一个空串A$,每次从$S$的头部拿出一个字符串可以放入$A$的头部和尾部,那么在这个过程中有多少情况下$T$是$A$的一个前缀。
$Solve:$
区间DP
设$f[i][j]为T[i]-T[j]构成的字符串是A的前缀的方案数$
枚举$S$,与一般的区间DP不同的是,$S_i即代表了区间的长度为i$,所以我们可以少一维循环。
状态转移:
$f[l][r] = f[l][r] + f[l + 1][r](S[i] = T[l])$
$f[l][r] = f[l][r] + f[l][r - 1](S[i] = T[r])$
由于是前缀且$lenT leq lenS$,所以在长度超过$lenT$之后,同样满足上述方程。
$Code:$
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 3010, mod = 998244353; 4 5 char s[N], t[N]; 6 int f[N][N]; 7 8 int main() 9 { 10 scanf("%s %s", s + 1, t + 1); 11 int n = strlen(s + 1); 12 int m = strlen(t + 1); 13 14 for(int i = 1; i <= n + 1; i ++) 15 f[i][i - 1] = 1; 16 17 for(int i = 1; i <= n; i ++) 18 { 19 for(int l = 1; l + i - 1 <= n; l ++) 20 { 21 int r = l + i - 1; 22 if(s[i] == t[l] || l > m) 23 f[l][r] = (f[l][r] + f[l + 1][r]) % mod; 24 if(s[i] == t[r] || r > m) 25 f[l][r] = (f[l][r] + f[l][r - 1]) % mod; 26 } 27 } 28 29 int ans = 0; 30 for(int i = m; i <= n; i ++) 31 ans = (ans + f[1][i]) % mod; 32 cout << ans << endl; 33 34 return 0; 35 }
$Description:$
交互式问题
有一个整数集合,范围为:$[0, n]$,对于某个数而言,个数最少$0$个,最多是$n$个,我们的目标就是求出$[0,n]$中每个数的个数。
已知$T:$三元组个数(元组的数相同)例:$(1, 1, 1)$
$S:$三元组个数(元组的数依次递增)例:$(2, 3, 4)$
其中对于集合$(1, 1, 1, 1, 2, 3)$
$T = 4$, $S = 4$
我们每次向集合中插入一个$[0, n]$的数,程序会反馈$T和S$给我们(已知初始集合的$T和S$)。
可以插入$n$个数
$Solve:$
插入顺序:$n-1,n-2,n-3......5,4,3,1,2,1$
按照这样的插入顺序我们可以轻易的求出$[1,4]$的个数,然后按顺序从小到大依次求出其他数。
显然我们是离线处理的
由于插入了两次$1$,所以可以简单的求出$1$的个数:
对于某一个数来说:$T = C_{n}^{3}$
插入这个数之后:$T = C_{n+1}^{3}$
相减即 $frac{n imes (n - 1)}{2} = riangle T$
对于其他数来说,要通过$S$来求:
举例连续的五个数:$1, 2, 3, 4, 5$,个数分别为:$a, b, c, d, e$
我们插入$3$,显然$ riangle S = a imes b + b imes d + d imes e$
根据已经求出来的数来算未知的数即可。
$Code:$
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 110; 4 5 int n; 6 int t0, s0; 7 int t[N], s[N], ans[N]; 8 9 void ask(int x, int &a, int &b) 10 { 11 printf("+ %d ", x); 12 fflush(stdout); 13 scanf("%d %d", &a, &b); 14 a -= t0, b -= s0; 15 t0 += a, s0 += b; 16 } 17 18 int cal(int x) 19 { 20 for(int i = 1; ; i ++) 21 if(i * (i - 1) == 2 * x) 22 return i; 23 } 24 25 int main() 26 { 27 scanf("%d%d%d", &n, &t0, &s0); 28 for(int i = n - 1; i >= 3; i --) ask(i, t[i], s[i]); 29 int a, b; 30 ask(1, t[1], s[1]); ask(2, t[2], s[2]); ask(1, a, b); 31 ans[1] = cal(a) - 1; 32 ans[3] = b - s[1] - 1; 33 ans[2] = s[1] / (ans[3] + 1); 34 ans[4] = s[2] / (ans[3] + 1) - ans[1] - 2; 35 for(int i = 5; i <= n; i ++) 36 ans[i] = (s[i - 2] - ans[i - 4] * ans[i - 3] - ans[i - 3] * (ans[i - 1] + 1)) / (ans[i - 1] + 1) - 1; 37 printf("!"); 38 ans[n] ++; 39 for(int i = 1; i <= n; i ++) printf(" %d", ans[i]); 40 return 0; 41 }
以上是关于#635 div2 A - F 题解的主要内容,如果未能解决你的问题,请参考以下文章
CF R 635 div2 1337D Xenia and Colorful Gems 贪心 二分 双指针