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 }
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 }