CCPC2018-湖南全国邀请赛 Solution
A - Easy $h$-index
后缀扫一下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 200010 6 7 int n; 8 ll arr[N]; 9 10 inline int work() 11 { 12 ll sum = 0; 13 for (int i = n; i >= 0; --i) 14 { 15 sum += arr[i]; 16 if (sum >= i) return i; 17 } 18 return 0; 19 } 20 21 int main() 22 { 23 while (scanf("%d", &n) != EOF) 24 { 25 for (int i = 0; i <= n; ++i) scanf("%lld", arr + i); 26 printf("%d\n", work()); 27 } 28 return 0; 29 }
B - Higher $h$-index
题意:有n个小时的工作量,在一篇paper上工作x小时,就能得到$a \cdot x$ 次引用,每增加一篇paper ,前面的paper 引用次数就+1 ,求h-index
思路:给每一篇paper一个小时,那么arr[]相当于 011111111111 一共有 a + n - 1 个1
然后要满足不等式$a + n - 1 - h + 1 >= h$
化简后便是 $h <= \frac{a + n}{2}$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 6 ll a, b; 7 8 int main() 9 { 10 while (scanf("%lld%lld", &a, &b) != EOF) 11 { 12 printf("%lld\n", (a + b) / 2); 13 } 14 return 0; 15 }
C - Just $h$-index
题意:给n个paper,给出每个paper的引用次数,每次询问给出 l, r 求只有这个区间内的paper有效,有h-index
思路:主席树维护,二分h 复杂度$O(n * log(n)^2)$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 #define M N * 30 6 #define ll long long 7 8 int n, q, u, v; 9 int arr[N]; 10 int T[N], L[M], R[M], C[M], tot; 11 12 inline int build(int l, int r) 13 { 14 int root = tot++; 15 C[root] = 0; 16 if (l < r) 17 { 18 int mid = (l + r) >> 1; 19 L[root] = build(l, mid); 20 R[root] = build(mid + 1, r); 21 } 22 return root; 23 } 24 25 inline int update(int root, int pos) 26 { 27 int newroot = tot++, tmp = newroot; 28 C[newroot] = C[root] + 1; 29 int l = 1, r = n; 30 while (l < r) 31 { 32 int mid = (l + r) >> 1; 33 if (pos <= mid) 34 { 35 L[newroot] = tot++, R[newroot] = R[root]; 36 newroot = L[newroot], root = L[root]; 37 r = mid; 38 } 39 else 40 { 41 L[newroot] = L[root], R[newroot] = tot++; 42 newroot = R[newroot], root = R[root]; 43 l = mid + 1; 44 } 45 C[newroot] = C[root] + 1; 46 } 47 return tmp; 48 } 49 50 inline int query(int left_root, int right_root, int pos) 51 { 52 int res = 0; 53 int l = 1, r = n; 54 while (l < r) 55 { 56 int mid = (l + r) >> 1; 57 if (pos <= mid) 58 { 59 left_root = L[left_root]; 60 right_root = L[right_root]; 61 r = mid; 62 } 63 else 64 { 65 res += C[L[left_root]] - C[L[right_root]]; 66 left_root = R[left_root]; 67 right_root = R[right_root]; 68 l = mid + 1; 69 } 70 } 71 return res; 72 } 73 74 75 int main() 76 { 77 while (scanf("%d%d", &n, &q) != EOF) 78 { 79 for (int i = 1; i <= n; ++i) scanf("%d", arr + i); 80 tot = 0; T[n + 1] = build(1, n); 81 for (int i = n; i >= 1; --i) T[i] = update(T[i + 1], arr[i]); 82 for (int i = 1, u, v; i <= q; ++i) 83 { 84 scanf("%d%d", &u, &v); 85 int l = 1, r = v - u + 1, ans; 86 while (r - l >= 0) 87 { 88 int mid = (l + r) >> 1; 89 int tot = v - u + 1 - query(T[u], T[v + 1], mid); 90 if (tot >= mid) 91 { 92 ans = mid; 93 l = mid + 1; 94 } 95 else 96 r = mid - 1; 97 } 98 printf("%d\n", ans); 99 } 100 101 } 102 return 0; 103 }
D - Circular Coloring
留坑。
E - From Tree to Graph
题意:给出一棵树 定义$f_i[u] = 去掉点u后的联通分量个数$ 定义$z_i = f_i(0) \oplus f_i(1) \oplus ... \oplus f_i(n - 1)$
$x_i = (a \cdot x_{i - 1} + b \cdot y_{y - 1} + z_{i - 1}) \pmod n$
$y_i = (b \cdot x_{i - 1} + a \cdot y_{i - 1} + z_{i - 1}) \pmod n$
求 $x_m, y_m$
思路:先求出$z_0$ 刚开始的联通分量个数就是每个点的出入度,然后根据异或性质,异或两次相当于消除,每次假如一条边必定形成一个环,然后并查集缩点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 10010 5 6 struct Edge 7 { 8 int to, nx; 9 inline Edge() {} 10 inline Edge(int to, int nx) : to(to), nx(nx) {} 11 }edge[N << 1]; 12 13 int n, m, a, b, x, y, ans; 14 int head[N], pos, sz[N]; 15 int rmq[N << 1], F[N << 1], P[N], fa[N], deep[N], cnt; 16 int pre[N]; 17 18 struct ST 19 { 20 int mm[N << 1]; 21 int dp[N << 1][20]; 22 inline void init(int n) 23 { 24 mm[0] = -1; 25 for (int i = 1; i <= n; ++i) 26 { 27 mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1]; 28 dp[i][0] = i; 29 } 30 for (int j = 1; j <= mm[n]; ++j) 31 { 32 for (int i = 1; i + (1 << j) - 1 <= n; ++i) 33 { 34 dp[i][j] = rmq[dp[i][j - 1]] < rmq[dp[i + (1 << (j - 1))][j - 1]] ? dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1]; 35 } 36 } 37 } 38 inline int query(int a, int b) 39 { 40 if (a > b) swap(a, b); 41 int k = mm[b - a + 1]; 42 return rmq[dp[a][k]] <= rmq[dp[b - (1 << k) + 1][k]] ? dp[a][k] : dp[b - (1 << k) + 1][k]; 43 } 44 }st; 45 46 inline void Init() 47 { 48 memset(head, -1, sizeof head); 49 memset(sz, 0, sizeof sz); 50 pos = 0; cnt = 0; ans = 0; 51 for (int i = 1; i <= n; ++i) pre[i] = i; 52 } 53 54 inline void addedge(int u, int v) 55 { 56 edge[++pos] = Edge(v, head[u]); head[u] = pos; 57 } 58 59 inline void DFS(int u) 60 { 61 F[++cnt] = u; 62 rmq[cnt] = deep[u]; 63 P[u] = cnt; 64 for (int it = head[u]; ~it; it = edge[it].nx) 65 { 66 int v = edge[it].to; 67 if (v == fa[u]) continue; 68 fa[v] = u; deep[v] = deep[u] + 1; 69 DFS(v); 70 F[++cnt] = u; 71 rmq[cnt] = deep[u]; 72 } 73 } 74 75 inline void init_lca(int root, int node_num) 76 { 77 fa[root] = 0; deep[0] = 0; 78 DFS(root); 79 st.init(2 * node_num - 1); 80 } 81 82 inline int query_lca(int u, int v) 83 { 84 return F[st.query(P[u], P[v])]; 85 } 86 87 inline int find(int x) 88 { 89 if (pre[x] != x) 90 pre[x] = find(pre[x]); 91 return pre[x]; 92 } 93 94 inline void join(int x, int y) 95 { 96 x = find(x); 97 if (deep[fa[x]] <= deep[y] || fa[x] == 0) return; 98 ans ^= sz[fa[x]] ^ (sz[fa[x]] - 1); 99 --sz[fa[x]]; 100 pre[x] = fa[x]; 101 join(fa[x], y); 102 } 103 104 inline void Run() 105 { 106 while (scanf("%d%d%d%d%d%d", &n, &m, &a, &b, &x, &y) != EOF) 107 { 108 Init(); 109 for (int i = 1, u, v; i < n; ++i) 110 { 111 scanf("%d%d", &u, &v); ++u, ++v; ++sz[u], ++sz[v]; 112 addedge(u, v); addedge(v, u); 113 } 114 init_lca(1, n); 115 for (int i = 1; i <= n; ++i) ans ^= sz[i]; 116 for (int i = 1; i <= m; ++i) 117 { 118 int nx = (a * x + b * y + ans) % n; 119 int ny = (b * x + a * y + ans) % n; 120 x = nx, y = ny; 121 join(x + 1, query_lca(x + 1, y + 1)); 122 } 123 printf("%d %d\n", x, y); 124 } 125 } 126 127 int main() 128 { 129 #ifdef LOCAL 130 freopen("Test.in", "r", stdin); 131 #endif 132 133 Run(); 134 return 0; 135 }
F - Sorting
公式化简,结构体排序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 1010 6 7 struct node 8 { 9 int id; 10 ll a, b, c, sum; 11 inline void scan(int _id) 12 { 13 scanf("%lld%lld%lld", &a, &b, &c); 14 sum = a + b; 15 id = _id; 16 } 17 inline bool operator < (const node &r) const 18 { 19 ll t1 = sum * r.c, t2 = r.sum * c; 20 return t1 < t2 || t1 == t2 && id < r.id; 21 } 22 }arr[N]; 23 24 int n; 25 26 int main() 27 { 28 while (scanf("%d", &n) != EOF) 29 { 30 for (int i = 1; i <= n; ++i) arr[i].scan(i); 31 sort(arr + 1, arr + 1 + n); 32 for (int i = 1; i <= n; ++i) printf("%d%c", arr[i].id," \n"[i == n]); 33 } 34 return 0; 35 }
G - String Transformation
题意:给出一个串a和串b,每次可以在串a中添加 {"aa", "bb", "abab"} 问能否将串a变成串b
思路:显然 两个串的c的数量不同是不可以变的 然后以c为间隔,分成若干个间隔,每个间隔里面的a,b差值不为偶数也是没法变
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 const int maxn = 1e4 + 10; 6 7 int a1[maxn], b1[maxn], c1[maxn]; 8 int a2[maxn], b2[maxn], c2[maxn]; 9 char str1[maxn], str2[maxn]; 10 11 int main() 12 { 13 while(~scanf("%s", str1 + 1)) 14 { 15 scanf("%s", str2 + 1); 16 int n = strlen(str1 + 1); 17 int m = strlen(str2 + 1); 18 int cnt1 = 0; 19 for(int i = 1; i <= n; ++i) 20 { 21 cnt1 += (str1[i] == 'c'); 22 } 23 int cnt2 = 0; 24 for(int i = 1; i <= m; ++i) 25 { 26 cnt2 += (str2[i] == 'c'); 27 } 28 if(cnt1 != cnt2) 29 { 30 puts("No"); 31 continue; 32 } 33 c1[0] = c2[0] = 0; 34 cnt1 = cnt2 = 1; 35 for(int i = 1; i <= n; ++i) 36 { 37 a1[i] = a1[i - 1] + (str1[i] == 'a'); 38 b1[i] = b1[i - 1] + (str1[i] == 'b'); 39 if(str1[i] == 'c') 40 { 41 c1[cnt1++] = i; 42 } 43 } 44 c1[cnt1++] = n; 45 for(int i = 1; i <= m; ++i) 46 { 47 a2[i] = a2[i - 1] + (str2[i] == 'a'); 48 b2[i] = b2[i - 1] + (str2[i] == 'b'); 49 if(str2[i] == 'c') 50 { 51 c2[cnt2++] = i; 52 } 53 } 54 c2[cnt2++] = m; 55 bool flag = true; 56 for(int i = 1; i < cnt1; ++i) 57 { 58 if((a1[c1[i]] - a1[c1[i - 1]]) % 2 != (a2[c2[i]] - a2[c2[i - 1]]) % 2 || (b1[c1[i]] - b1[c1[i - 1]]) % 2 != (b2[c2[i]] - b2[c2[i - 1]]) % 2) 59 { 60 flag = false; 61 } 62 } 63 puts(flag ? "Yes" : "No"); 64 } 65 return 0; 66 }
H - Infinity
留坑。
I - Longest Increasing Subsequence
题意:给出n个数,有若干个数是0,定义$F[i]$ 为将所有0变成i后的最长上升子序列长度 求$\sum_{i = 1}^{i = n} i \cdot F[i]$
思路:预处理出$dp[], dp2[]$ $dp[i]$表示以$i$结尾的最长上升子序列长度,$dp2[i]$ 表示以$i$开头的最长上升子序列长度
我们考虑$F[i] = (Minlen, Minlen + 1)$
我们先求出 去掉所有0的最长上升子序列长度 然后考虑 $dp[i] + dp2[j] = m$ 并且 $[i, j]$ 中有0存在 并且 $arr[i] < arr[j]$ 那么
$x \in [arr[i] + 1, arr[j] - 1]$ 的时候 $F[x] = len + 1$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 #define INF 0x3f3f3f3f 6 #define ll long long 7 8 int n, m; 9 int dp[N], dp2[N], arr[N], brr[N], a[N]; 10 11 inline void LIS() 12 { 13 int len = 0; brr[1] = 0; 14 for (int i = 1; i <= n; ++i) 15 { 16 if (arr[i] == 0) continue; 17 int pos = lower_bound(brr + 1, brr + 1 + len, arr[i]) - brr; 18 if (pos > len) ++len; 19 dp[i] = pos; brr[pos] = arr[i]; 20 } 21 m = len; len = 0; brr[1] = 0; 22 for (int i = n; i >= 1; --i) 23 { 24 if (arr[i] == 0) continue; 25 int pos = lower_bound(brr + 1, brr + 1 + len, -arr[i]) - brr; 26 if (pos > len) ++len; 27 dp2[i] = pos; brr[pos] = -arr[i]; 28 } 29 } 30 31 32 inline void Run() 33 { 34 while (scanf("%d", &n) != EOF) 35 { 36 for (int i = 1; i <= n; ++i) scanf("%d", arr + i); LIS(); 37 memset(a, 0, sizeof a); memset(brr, 0x3f, sizeof brr); brr[0] = 0; 38 int now = 0; 39 for (int i = 1; i <= n; ++i) 40 { 41 if (arr[i] == 0) 42 { 43 for (int j = now + 1; j < i; ++j) 44 { 45 brr[dp[j]] = min(brr[dp[j]], arr[j]); 46 } 47 now = i; 48 continue; 49 } 50 if (now && brr[m - dp2[i]] < arr[i]) 51 { 52 a[brr[m - dp2[i]] + 1]++; 53 a[arr[i]]--; 54 } 55 } 56 if (now && brr[m] != INF) 57 { 58 a[brr[m] + 1]++; 59 } 60 for (int i = 1; i <= n; ++i) a[i] += a[i - 1]; 61 ll ans = 0; 62 for (int i = 1; i <= n; ++i) ans += (ll)i * (m + (a[i] ? 1 : 0)); 63 printf("%lld\n", ans); 64 } 65 } 66 67 int main() 68 { 69 #ifdef LOCAL 70 freopen("Test.in", "r", stdin); 71 #endif 72 73 Run(); 74 return 0; 75 }
J - Vertex Cover
题意:有n个点,标号为0 - n - 1每个点的权值就是$2_i$ i 为标号,alice 任意选择一种边集,bob选择一种权值和最小的边际覆盖它的边集,覆盖的定义为alice中每条边中至少有一个点在bob的边集当中,并且权值和为k
思路:因为权值为2的幂,所以如果bob存在一种选择满足要求,只有一种,那就是k
那么相当于固定bob的选择,找alice有多少种选择被bob覆盖
从左往右扫,如果遇到一个点是0,那么这个点可以和前面的1连一条边
如果遇到一个点是1,那么这个点至少要在前面选择一个没有选的点相连,其他点可连可不连,所有可能性相乘
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define MOD 1000000007 6 #define N 100010 7 8 ll Bit[N]; 9 10 inline void Init() 11 { 12 Bit[0] = 1; 13 for (int i = 1; i <= 100000; ++i) Bit[i] = (Bit[i - 1] << 1) % MOD; 14 } 15 16 int n; 17 char s[N]; 18 19 int main() 20 { 21 Init(); 22 while (scanf("%d", &n) != EOF) 23 { 24 scanf("%s", s); 25 int len = strlen(s); 26 ll ans = 1; int k = n - len, cnt = 0; 27 for (int i = 0; i < len; ++i, ++k) 28 { 29 if (s[i] == '1') 30 { 31 ans = (ans *(Bit[k] - Bit[cnt] + MOD) % MOD) % MOD; 32 ++cnt; 33 } 34 else 35 { 36 ans = (ans * Bit[cnt]) % MOD; 37 } 38 } 39 printf("%lld\n", ans); 40 } 41 return 0; 42 }
K - 2018
题意:给出a, b, c, d, 找出有多少个二元组(x, y) 满足 $ x \cdot y \equiv 0 \pmod 2018$
思路:显然,2018只能拆成1009 * 2
那么只要在(a, b) 中有多少1009 倍数 2008倍数 然后计算 注意去重
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 6 ll x, y; 7 ll a, b, c, d; 8 9 int main() 10 { 11 while (scanf("%lld%lld%lld%lld", &a, &b, &c, &d) != EOF) 12 { 13 ll ans = 0; 14 15 x = (b / 2018) - ((a - 1) / 2018); 16 y = (d - c + 1); 17 ans += x * y; 18 19 x = (d / 2018) - ((c - 1) / 2018); 20 y = (b - a + 1); 21 ans += x * y; 22 23 x = (b / 2018) - ((a - 1) / 2018); 24 y = (d / 2018) - ((c - 1) / 2018); 25 ans -= x * y; 26 27 x = (((b / 1009) + 1) / 2) - ((((a - 1) / 1009) + 1) / 2); 28 y = ((d / 2) - ((c - 1) / 2)) - ((d / 2018) - ((c - 1) / 2018)); 29 ans += x * y; 30 31 x = (((d / 1009) + 1) / 2) - ((((c - 1) / 1009) + 1) / 2); 32 y = ((b / 2) - ((a - 1) / 2)) - ((b / 2018) - ((a - 1) / 2018)); 33 ans += x * y; 34 35 printf("%lld\n", ans); 36 } 37 return 0; 38 }