Mail.Ru Cup 2018 Round 1

A. Elevator or Stairs?

签.

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 int x, y, z, t[3];
 5 
 6 int main()
 7 {
 8     while (scanf("%d%d%d", &x, &y, &z) != EOF)
 9     {
10         for (int i = 0; i < 3; ++i) scanf("%d", t + i);
11         int a = (abs(z - x) + abs(x - y)) * t[1] + 3 * t[2];
12         int b = abs(x - y) * t[0];
13         puts(a <= b ? "YES" : "NO");
14     }
15     return 0;
16 }
View Code

 

 

B. Appending Mex

签.

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 100010
 5 int n, a[N], vis[N], last;
 6 
 7 void solve()
 8 {
 9     for (int i = 1; i <= n; ++i)
10     {
11         while (vis[last + 1]) ++last;
12         if (a[i] > last + 1) 
13         {
14             printf("%d\n", i);
15             return;
16         }
17         vis[a[i]] = 1;
18     }
19     puts("-1");
20 }
21 
22 int main()
23 {
24     while (scanf("%d", &n) != EOF)
25     {
26         memset(vis, 0, sizeof vis); last = -1;
27         for (int i = 1; i <= n; ++i) scanf("%d", a + i);
28         solve();
29     }
30     return 0;
31 }
View Code

 

C. Candies Distribution

Upsolved.

题意:

一个序列,$a_i取值为[1, n]$

告诉你$每个数左边有多少数大于你,记为l_i$

$右边有多少数大于你,记为r_i$

让你还原出这个序列,如果没有合法的输出$NO$

思路:

$我们知道一个数的l_i + r_i 越大,那么这个数越小$

$那我们不妨令 a_i = n - l_i - r_i$

$再检查一遍即可$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define N 1010
 5 int n, l[N], r[N], v[N];
 6 
 7 int main()
 8 {
 9     while (scanf("%d", &n) != EOF)
10     {
11         for (int i = 1; i <= n; ++i) scanf("%d", l + i);
12         for (int i = 1; i <= n; ++i) scanf("%d", r + i);
13         for (int i = 1; i <= n; ++i) v[i] = n - l[i] - r[i];
14         bool flag = true;
15         for (int i = 1; i <= n; ++i) 
16         {
17             for (int j = 1; j < i; ++j)
18                 if (v[j] > v[i])
19                     --l[i];
20             for (int j = i + 1; j <= n; ++j)
21                 if (v[j] > v[i])
22                     --r[i];
23             if (l[i] != 0 || r[i] != 0)
24             {
25                 flag = false;
26                 break;
27             }
28         }
29         if (!flag) puts("NO");
30         else
31         {
32             puts("YES");
33             for (int i = 1; i <= n; ++i) 
34                 printf("%d%c", v[i], " \n"[i == n]);
35         }
36     }
37     return 0;
38 }
View Code

 

 

D. Changing Array

Upsolved.

题意:

给一个序列,$可以将每个位置上的数取反$

$求最多有多少子区间的异或和不为0$

思路:

求最多有多少子区间$异或和不为0,比较困难$

$正难则反,我们考虑反面,我们求最少有多少个子区间异或和为0$

$我们知道一段区间l, r的异或和是S_r \oplus S_[l - 1]$

$S表示前缀异或$

$那么一个序列的前缀异或里面如果有x个a, 那么子区间异或为0的个数为\frac{a \cdot (a - 1)}{2}$

$我们考虑 b = ~a, 而且a, b之间可以互相转化,并且只能互相转化,而不能转化为其他的数字$

$令x = a的个数, y = b的个数$

$我们令n = x + y $

$那么a, b构成的子区间个数就是 \frac{x \cdot (x - 1)}{2} + \frac{y \cdot (y - 1)}{2}$

$将n = x + y 代入$

$就得到一个一元二次方程,取对称轴即可$

$我们考虑转化都是\oplus (1 << k) - 1$

转化两次即相当于没有转化

$所以每个前缀异或都能转化为自己想要的那个数$

$为什么不考虑前面的数转化了会影响到后面的前缀异或?$

$那后面那个如果受影响了,再转化回来不就好了?$

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 #define ll long long
 5 #define N 200010
 6 int n, k;
 7 int a[N];
 8 map <int, int> mp;
 9 
10 int main()
11 {
12     while (scanf("%d%d", &n, &k) != EOF)
13     {
14         mp.clear(); mp[0] = 1;
15         int d = (1 << k) - 1;
16         for (int i = 1; i <= n; ++i) scanf("%d", a + i);
17         for (int i = 1; i <= n; ++i)
18         {
19             a[i] ^= a[i - 1];
20             ++mp[a[i]];
21             if (mp.find(a[i] ^ d) == mp.end()) mp[a[i] ^ d] = 0;
22         }
23         ll res = 0;
24         for (auto it : mp) if (it.first > (it.first ^ d))
25         {
26             int a = it.second, n = a + mp[it.first ^ d];
27             int b = n / 2;
28             res += (1ll * n * n + 2ll * b * b - 2ll * b * n - n) / 2;
29         }
30         printf("%lld\n", 1ll * n * (n + 1) / 2 - res);
31     }
32     return 0;
33 }
View Code

 

posted @ 2019-02-07 07:25  Dup4  阅读(138)  评论(0编辑  收藏  举报