Codeforces Round 696(Div.2)


A、Puzzle From the Future

题意:

给两个长度相同的$01$串$a$和$b$,把$a$和$b$做不进位加法,然后结果中连续相同的数字缩成一个数字,设这个数字是$d$。给出$b$,问$d$最大时$a$的情况。

题解:

显然不能出现相同连续数字最优,直接根据前一位数字和当前$a_i$和$b_i$能达到的最大情况分情况讨论即可。

B、Different Divisors

题意:

给出$d$,求最小的$a$,使得$a$的因数任意两两之差不小于$d$,且至少有$4$个不同因数。

题解:

筛素数,然后$1$肯定选,选出距离$1+d$且大于等于$1+d$的素数$p$,同理找出$q \geq 1 + p$,结果就是$p \times q$。

C、Array Destruction

题意:

给一个长度是$2 \times n$的数组,在开始前选一个$x$,每步删除时,先删除数组中任意两个和是$x$的数,然后$x$变成较大的那个数,继续删除,直到所有的数都删完,求删除的策略。

题解:

比赛的时候我往$dfs$的方向想了,但是$dfs$是大材小用。首先显然的性质就是,每次删除必选没有删掉的数中最大的那个。此时,除了第一次删,剩下那个数也确定了,但是题目样例$4$中,如果删了$11$之后选$4$和$7$,那么会删不完,选$5$和$6$才能删完,因此$7$就要搭配最大的删掉,但是我们事先并不知道需要删掉这个$7$,只能枚举所有的数组中的非最大值。找有没有两个数加起来等于当前的$x$可以用$multiset$也可以用桶,$multiset$记得删除时删迭代器,删数将导致所有相同的数都会被删掉。

AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e3 + 5;
 4 
 5 bool multi = 1;
 6 int a[N << 1];
 7 multiset<int> s;
 8 vector<pair<int, int>> v;
 9 
10 void solve()
11 {
12     s.clear();
13     int n;
14     scanf("%d", &n);
15     n <<= 1;
16     for (int i = 1; i <= n; ++i)
17         scanf("%d", a + i), s.insert(a[i]);
18     sort(a + 1, a + n + 1);
19     for (int i = 1; i < n; ++i)
20     {
21         v.clear();
22         multiset<int> s1 = s;
23         auto it = s1.find(a[i]);
24         int t1;
25         if (it != s1.end())
26         {
27             t1 = *it;
28             s1.erase(it);
29         }
30         it = --s1.end();
31         int cur = *it;
32         v.push_back({t1, cur});
33         s1.erase(it);
34         while (s1.size())
35         {
36             int tmp = cur - *(--s1.end());
37             cur = *(--s1.end());
38             s1.erase(--s1.end());
39             it = s1.find(tmp);
40             if (it != s1.end())
41             {
42                 t1 = *it;
43                 s1.erase(it);
44                 v.push_back({t1, cur});
45             }
46             else
47                 break;
48         }
49         if (s1.size() == 0)
50         {
51             printf("YES\n");
52             printf("%d\n", v[0].first + v[0].second);
53             for (auto &i : v)
54                 printf("%d %d\n", i.first, i.second);
55             return;
56         }
57     }
58     printf("NO\n");
59 }
60 int main()
61 {
62     int t;
63     if (multi)
64         scanf("%d", &t);
65     else
66         t = 1;
67     while (t--)
68         solve();
69     return 0;
70 }
View Code

D、Cleaning

题意:

给$n$个数,每次选相邻的两个数都减一,当第$i$堆减到$0$时,第$i-1$和第$i+1$堆也不算是相邻,问在删除前允许交换任意相邻数一次的前提下能不能把所有数归零。

题解:

显然的结论,如果所有数都能归零时,从两边开始归零是一定可以的。假设一个序列能归零,则对于左边:$a_2' = a_2 - a_1$,$a_3' = a_3 - a_2' -> a_3 - (a_2 - a_1)$,$a_4' = a_4 - a_3' = a_4 - (a_3 - (a_2 - a_1))$。令$l_i = a_i - a_{i-1}$,意义是$a_i$被$a_{i-1}$删完了之后还剩多少,则$a_i' = a_i - p_{i-1}$,如果$a_i > a_{i+1}$,说明$a_i$必不可能被删完,此时$l_i = -1$,如果$l_{i-1} = -1$,则$l_i = -1$。因为如果是前面删不完,后面的$l_i$没有意义。右边同理。我们发现,交换$a_i$和$a_{i+1}$时,它左边的直到$l_{i-1}$和右边的直到$r_{i+2}$不会受影响,所以只需要判断$a_i,a_{i+1},l_{i-1},r_{i+2}$的关系即可。显然,如果可行,一定有一个位置有$l_i = r_{i+1}$。为了考虑边界,即交换$a_1,a_2$或交换$a_{n-1},a_n$的情况。我们在数组两边个增加一个$l_0 = a_0 = 0,r_{n+1} =  a_{n+1} = 0$。

 AC代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 2e5 + 5;
 4 typedef long long ll;
 5 
 6 bool multi = 1;
 7 ll a[N], l[N], r[N];
 8 void solve()
 9 {
10     int n;
11     scanf("%d", &n);
12     memset(l, 0, sizeof(l[0]) * (n + 2));
13     memset(r, 0, sizeof(r[0]) * (n + 2));
14     a[n + 1] = 0;
15     for (int i = 1; i <= n; ++i)
16         scanf("%lld", &a[i]);
17     l[0] = a[0];
18     for (int i = 1; i < n + 2; ++i)
19     {
20         if (l[i - 1] == -1 || l[i - 1] > a[i])
21             l[i] = -1;
22         else
23             l[i] = a[i] - l[i - 1];
24     }
25     r[n + 1] = a[n + 1];
26     for (int i = n; i >= 0; --i)
27     {
28         if (r[i + 1] == -1 || r[i + 1] > a[i])
29             r[i] = -1;
30         else
31             r[i] = a[i] - r[i + 1];
32     }
33     for (int i = 0; i < n + 1; ++i)
34         if (l[i] != -1 && r[i + 1] != -1 && l[i] == r[i + 1])
35             return void(printf("YES\n"));
36     for (int i = 1; i < n; ++i)
37         if (l[i - 1] != -1 && r[i + 2] != -1 && l[i - 1] <= a[i + 1] && r[i + 2] <= a[i] && a[i + 1] - l[i - 1] == a[i] - r[i + 2])
38             return void(printf("YES\n"));
39     printf("NO\n");
40 }
41 int main()
42 {
43     int t;
44     if (multi)
45         scanf("%d", &t);
46     else
47         t = 1;
48     while (t--)
49         solve();
50     return 0;
51 }
View Code
posted @ 2021-01-20 23:08  Aya_Uchida  阅读(85)  评论(0编辑  收藏  举报