codeforces Round 649(div. 2)
A、XXXXX
题意:
在一个数组中找出连续的一段,使得其和不能被$x$整除,且长度尽可能长。
题解:
显然取全部的时候长度最长,如果这个时候不行,就分别枚举其前缀和和后缀和,取两边长度最大值,如果都找不到,输出$-1$。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 5; 4 int a[N]; 5 void solve() 6 { 7 int n, sum = 0, x; 8 scanf("%d%d", &n, &x); 9 for (int i = 1; i <= n; ++i) 10 scanf("%d", &a[i]), sum += a[i]; 11 if (sum % x) 12 { 13 printf("%d\n", n); 14 return; 15 } 16 int tmp = sum, i, ans = -1; 17 for (i = 1; i <= n && tmp % x == 0; ++i) 18 tmp -= a[i]; 19 ans = max(ans, n - i + 1); 20 tmp = sum; 21 for (i = n; i && tmp % x == 0; --i) 22 tmp -= a[i]; 23 ans = max(ans, i); 24 printf("%d\n", ans ? ans : ans - 1); 25 } 26 int main() 27 { 28 int T; 29 scanf("%d", &T); 30 while (T--) 31 solve(); 32 return 0; 33 }
B、Most socially-distanced subsequence
题意:
给出一个排列$p$,长度为$n$,求找出一个长度为$k$序列$s$,使$|s_1-s_2|+|s_2-s_3|+...+|s_{k-1}-s_k|$尽量大,满足前面条件,还要$s$的长度尽量小。
题解:
对于单调的子段,只取端点处是最优的,所以取所有的山峰,山谷的点,并且必取$p[1]$和$p[n]$。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 5; 4 int a[N]; 5 vector<int> v; 6 void solve() 7 { 8 int n; 9 scanf("%d", &n); 10 for (int i = 1; i <= n; ++i) 11 scanf("%d", &a[i]); 12 v.clear(); 13 v.push_back(a[1]); 14 for (int i = 2; i < n; ++i) 15 { 16 if (a[i] > a[i - 1] && a[i] > a[i + 1]) 17 v.push_back(a[i]); 18 else if (a[i] < a[i - 1] && a[i] < a[i + 1]) 19 v.push_back(a[i]); 20 } 21 v.push_back(a[n]); 22 printf("%d\n", (int)v.size()); 23 for (auto &i : v) 24 printf("%d ", i); 25 printf("\n"); 26 } 27 int main() 28 { 29 int T; 30 scanf("%d", &T); 31 while (T--) 32 solve(); 33 return 0; 34 }
*C、Ehab and Prefix MEXs
题意:
给出一个数组$a$,求相同长度的非负数组$b$,使得$b$中的每个元素都不大于$1e6$,且$mex(b_1,b_2,...,b_i)=a_i$。
题解:
自己想的时候总是往给$b$数组按顺序分配$1~n$再调整想了,然后发现好像不可做。因为这样子分配,如果有了冲突之后,修改的成本很大,会$TLE$。
注意到$a$数组是非递降的,且$a_i \leq i$,这就说明似乎可以考虑双指针?考虑在当前位置,$a_i$和$b_i$必不相同,所以我们考虑把出现在$a$数组的数,和不出现在$a$数组的数分成两组,都按升序排,分别称为$1$,组$2$,如果组$1$当前没有选完,并且$a_i>group1[pos1]$,说明当前的组$1$这个数已经在$a$中出现了(重复的话,这个位置直接填$a$中没出现过的或者无穷大($1e6$)),所以自然可以尝试填进去。然后填完之后检查可行性即可。可行性的检查就是直接遍历从$0$开始的每一个数,然后看看是不是$a_i-1$就是当前的遍历结果,如果出现一个失败,就是不存在。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 5; 4 const int INF = 1e5 + 1; 5 int buc[N << 4]; 6 int a[N], b[N], g1[N], g2[N]; 7 int main() 8 { 9 int n; 10 scanf("%d", &n); 11 for (int i = 1; i <= n; ++i) 12 { 13 scanf("%d", &a[i]); 14 ++buc[a[i]]; 15 } 16 int cnt1 = 0, cnt2 = 0; 17 for (int i = 0; i <= a[n]; ++i) 18 { 19 if (buc[i]) 20 g1[++cnt1] = i; 21 else 22 g2[++cnt2] = i; 23 } 24 int pos1 = 1, pos2 = 1; 25 for (int i = 1; i <= n; ++i) 26 { 27 if (pos1 <= cnt1 && a[i] > g1[pos1]) 28 b[i] = g1[pos1++]; 29 else if (pos2 <= cnt2) 30 b[i] = g2[pos2++]; 31 else 32 b[i] = INF; 33 } 34 memset(buc, 0, sizeof(buc)); 35 int tmp = -1; 36 for (int i = 1; i <= n; ++i) 37 { 38 buc[b[i]] = 1; 39 while (buc[tmp + 1]) 40 ++tmp; 41 if (a[i] != tmp + 1) 42 return printf("-1\n"), 0; 43 } 44 for (int i = 1; i <= n; ++i) 45 printf("%d%c", b[i], " \n"[i == n]); 46 return 0; 47 }
注意:还是要多注意题目的条件,这样子可以找到正解的切入点。
*D、Ehab's Last Corollary
题意:
给出一个图,找出一个至多有$k$个点的环或者找到一个有$\lceil \frac{k}{2} \rceil$个点的独立集(即集中任何两个元素没有边直接相连)。
题解:
找环是比较简单的,所以考虑先找环。然后注意到:如果找到的最小环都比$k$大,那么只要我对这个环隔一个点输出一个点,一定能找到这个独立集。所以题目就转化成,找个环就行了。找最小环通过$dfs树$即可,考虑到图可能会变成树,但是树的$dfs树$是它本身,所以我们万一找不到适合的环,我们可以考虑如何在树上找独立集,然后推广到图的$dfs$树上。对于树的每一层的结点数一定比它的下一层的结点数少,所以我们回溯的时候把所有叶节点染白,然后白点的邻接点全部染黑,在白色的结点中找这个独立集。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 1e5 + 5; 4 vector<int> G[N]; 5 bool vis[N]; 6 vector<int> cyc; 7 int dep[N]; 8 int n, m, k; 9 void dfs(int u, int fa) 10 { 11 cyc.push_back(u); 12 dep[u] = dep[fa] + 1; 13 for (auto i : G[u]) 14 { 15 if (i == fa) 16 continue; 17 if (!dep[i]) 18 dfs(i, u); 19 else 20 { 21 if (dep[u] - dep[i] + 1 >= 0 && dep[u] - dep[i] + 1 <= k) 22 { 23 printf("2\n%d\n", dep[u] - dep[i] + 1); 24 for (int j = dep[i] - 1; j < dep[u]; ++j) 25 printf("%d%c", cyc[j], " \n"[j == dep[u] - 1]); 26 exit(0); 27 } 28 } 29 } 30 if (!vis[u]) 31 for (auto i : G[u]) 32 vis[i] = 1; 33 cyc.pop_back(); 34 } 35 int main() 36 { 37 scanf("%d%d%d", &n, &m, &k); 38 for (int i = 1; i <= m; ++i) 39 { 40 int u, v; 41 scanf("%d%d", &u, &v); 42 G[u].push_back(v); 43 G[v].push_back(u); 44 } 45 dfs(1, 0); 46 printf("1\n"); 47 for (int i = 1, cnt = 1; i <= n && cnt <= (k + 1) / 2; ++i) 48 if (!vis[i]) 49 printf("%d%c", i, " \n"[cnt == (k + 1) / 2]), ++cnt; 50 return 0; 51 }