#635 div2 A - F 题解

参考资料:http://codeforces.com/blog/entry/76099

 

A. Ichihime and Triangle

  $Description:$

    给你四个整数 $a, b, c, d$, 选择三个数$x, y, z$构成一个三角形,其中$a\leq x\leq b, b\leq y\leq c, c\leq z\leq 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\n", b, c, c);
12     }
13     return 0;
14 }
View Code

 

 B. Kana and Dragon Quest game

   $Description:$

    怪兽的血量为$x$,你有两种攻击方式:

    $1:x = \left \lfloor x / 2 \right \rfloor + 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 }
View Code

 

C. Linova and Kingdom

  $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 }
View Code

 

D. Xenia and Colorful Gems

   $Description:$

    你有三个正整数集合,从三个集合中分别选出一个数为$x, y, z$, 使得$(x - y)^2 + (x - z)^2 + (y - z) ^ 2$最小是多少。

  $Solve:$

    显然对于选出来的三个数一定存在以下大小关系的:

    $x\leq y\leq z$, $x\leq z\leq y$,$y\leq x\leq z$,$y\leq z\leq x$,$z\leq x\leq y$,$z\leq y\leq 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 }
View Code

 

E. Kaavi and Magic Spell

    $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 }
View Code

 

F. Yui and Mahjong Set

  $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 \times (n - 1)}{2} = \triangle T$

    对于其他数来说,要通过$S$来求:

      举例连续的五个数:$1, 2, 3, 4, 5$,个数分别为:$a, b, c, d, e$

      我们插入$3$,显然$\triangle S = a\times b + b\times d + d\times 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\n", 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 }
View Code
posted @ 2020-05-02 13:37  nonameless  阅读(124)  评论(0编辑  收藏  举报