2018中国大学生程序设计竞赛 - 网络选拔赛 Solution
A - Buy and Resell
题意:给出n个交易点,每次能够选择买或者卖,求获得最大利润
思路:维护两个优先队列,一个是卖,一个是替换,当价格差相同时,优先替换,因为次数要最少
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 100010 6 7 int t, n; 8 ll arr[N]; 9 priority_queue <ll, vector <ll>, greater <ll> > q[2]; 10 11 inline void Run() 12 { 13 scanf("%d", &t); 14 while (t--) 15 { 16 scanf("%d", &n); 17 for (int i = 1; i <= n; ++i) scanf("%lld", arr + i); 18 for (int i = 0; i < 2; ++i) while (!q[i].empty()) q[i].pop(); 19 ll ans = 0; 20 for (int i = 1; i <= n; ++i) 21 { 22 if (q[0].empty() && q[1].empty()) q[0].emplace(arr[i]); 23 else if (q[1].empty()) 24 { 25 ll top = q[0].top(); 26 if (arr[i] > top) 27 { 28 q[0].pop(); 29 ans += arr[i] - top; 30 q[1].emplace(arr[i]); 31 } 32 else 33 q[0].emplace(arr[i]); 34 } 35 else if (q[0].empty()) 36 { 37 ll top = q[1].top(); 38 if (arr[i] > top) 39 { 40 q[1].pop(); 41 ans += arr[i] - top; 42 q[0].emplace(top); 43 q[1].emplace(arr[i]); 44 } 45 else 46 { 47 q[0].emplace(arr[i]); 48 } 49 } 50 else 51 { 52 ll top1 = q[0].top(), top2 = q[1].top(); 53 if (top1 < top2) 54 { 55 if (arr[i] > top1) 56 { 57 q[0].pop(); 58 ans += arr[i] - top1; 59 q[1].emplace(arr[i]); 60 } 61 else 62 { 63 q[0].emplace(arr[i]); 64 } 65 } 66 else 67 { 68 if (arr[i] > top2) 69 { 70 q[1].pop(); 71 ans += arr[i] - top2; 72 q[0].emplace(top2); 73 q[1].emplace(arr[i]); 74 } 75 else 76 { 77 q[0].emplace(arr[i]); 78 } 79 } 80 } 81 } 82 printf("%lld %d\n", ans, q[1].size() * 2); 83 } 84 } 85 86 int main() 87 { 88 #ifdef LOCAL 89 freopen("Test.in", "r", stdin); 90 #endif 91 92 Run(); 93 94 return 0; 95 }
B - Congruence equation
留坑。
C - Dream
题意:给出一个p,重定义加法和乘法,使得$(m + n)^p = m^p + n^p $
思路:有费马小定理 $a^p \equiv a \pmod p$
那只需要重定义
$(m + n) = (m + n) \pmod p$
$(m \cdot n) = (m \cdot n) \pmod p$
原根可以保证两个集合相等
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 2010 6 7 int t, p; 8 int a[N][N], b[N][N]; 9 10 inline void Run() 11 { 12 scanf("%d", &t); 13 while (t--) 14 { 15 scanf("%d", &p); 16 for (int i = 0; i < p; ++i) 17 { 18 for (int j = 0; j < p; ++j) 19 { 20 a[i][j] = (i + j) % p; 21 b[i][j] = (i * j) % p; 22 } 23 } 24 for (int i = 0; i < p; ++i) 25 { 26 for (int j = 0; j < p; ++j) 27 { 28 printf("%d%c", a[i][j], " \n"[j == p - 1]); 29 } 30 } 31 for (int i = 0; i < p; ++i) 32 { 33 for (int j = 0; j < p; ++j) 34 { 35 printf("%d%c", b[i][j], " \n"[j == p - 1]); 36 } 37 } 38 } 39 } 40 41 int main() 42 { 43 #ifdef LOCAL 44 freopen("Test.in", "r", stdin); 45 #endif 46 47 Run(); 48 49 return 0; 50 }
D - Find Integer
题意:给出一个n和一个a 找出 $a^n + b^n = c^n$
思路:根据费马大定理 n > 2 无解 显然 n = 0 无解
n = 1 直接凑
n = 2 如果是奇数 有 $a^2 + (\frac{a \cdot a}{2})^2 = (\frac{(a \cdot a) + 1}{2}) ^2$
如果是偶数 一直除下去 知道是奇数 然后把多余的偶数加到b 和 c 上去
或者
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define INF 0x3f3f3f3f 6 #define N 40010 7 8 int t; 9 ll n, a, cnt; 10 11 ll arr[N][2]; 12 13 inline void Run() 14 { 15 for (int i = 3; i <= 40000; ++i) 16 { 17 cnt = 1; 18 ll a = i; 19 while ((a & 1) == 0 && a > 4) 20 { 21 cnt <<= 1; 22 a >>= 1; 23 } 24 if (a == 4) 25 { 26 arr[i][0] = cnt * 3, arr[i][1] = cnt * 4; 27 } 28 else 29 { 30 ll b = a * a / 2; 31 ll c = b + 1; 32 arr[i][0] = b * cnt; 33 arr[i][1] = c * cnt; 34 } 35 } 36 scanf("%d", &t); 37 while (t--) 38 { 39 scanf("%lld%lld", &n, &a); 40 if (n == 0 || n >= 3) 41 { 42 puts("-1 -1"); 43 continue; 44 } 45 if (n == 1) 46 { 47 printf("1 %lld\n", a + 1); 48 continue; 49 } 50 printf("%lld %lld\n", arr[a][0], arr[a][1]); 51 } 52 } 53 54 int main() 55 { 56 #ifdef LOCAL 57 freopen("Test.in", "r", stdin); 58 #endif 59 60 Run(); 61 62 return 0; 63 }
E - GuGu Convolution
留坑。
F - Neko and Inu
留坑。
G - Neko's loop
题意:给出一个长度为n的数列,每个位置都有自己的权值,你有m点能量,每次可以从i走到(i+k)%n点,可以再任意时刻停止,为达到s点能量,需要起始能量为多少。
思路:可以通过o(n)的时间处理出循环节。对于每个循环节,我们可以走完循环节或者不走完。对于不走完这部分的步数可能为m%循环节长度,也可能为m或者循环节长度。问题就转换为长度不超过限制长度的最大连续子序列,通过dp+单调队列来解决。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 const int MOD = 1e9 + 7; 7 const int INF = 0x3f3f3f3f; 8 const int maxn = 1e4 + 10; 9 10 int n, m, k; 11 ll s, ans, val[maxn]; 12 vector<ll>vec; 13 bool vis[maxn]; 14 ll sum[maxn << 1]; 15 ll que[maxn << 1]; 16 17 inline ll cal(int count) 18 { 19 int len = vec.size(); 20 for (int i = 0; i < len; ++i) 21 { 22 que[i] = que[i + len] = vec[i]; 23 } 24 len <<= 1; 25 list<ll>q; 26 int st = 0; 27 int ed = 0; 28 ll res = 0; 29 for (int i = 0; i < len; ++i) 30 { 31 if (i == 0) 32 { 33 sum[i] = que[i]; 34 } 35 else 36 { 37 sum[i] = sum[i - 1] + que[i]; 38 } 39 } 40 for (int i = 0; i < len; ++i) 41 { 42 while (!q.empty() && sum[q.front()] > sum[i]) 43 { 44 q.pop_front(); 45 } 46 q.push_front(i); 47 while (!q.empty() && i - q.back() > count) 48 { 49 q.pop_back(); 50 } 51 res = max(res, sum[i] - sum[q.back()]); 52 } 53 return res; 54 } 55 56 inline ll solve() 57 { 58 ll mod = m % vec.size(); 59 ll circle = m / vec.size(); 60 ll sum = 0; 61 for (auto it : vec) 62 { 63 sum += it; 64 } 65 ll max1 = cal(mod); 66 ll max2 = cal(vec.size()); 67 max1 += max(0ll, sum) * circle; 68 max2 += max(0LL, sum)*((circle > 2) ? circle - 1 : 0); 69 return max(max1, max2); 70 } 71 72 inline void RUN() 73 { 74 int t; 75 scanf("%d", &t); 76 for (int cas = 1; cas <= t; ++cas) 77 { 78 memset(vis, false, sizeof vis); 79 scanf("%d %lld %d %d", &n, &s, &m, &k); 80 for (int i = 0; i < n; ++i) 81 { 82 scanf("%lld", val + i); 83 } 84 ans = 0; 85 for (int i = 0; i < n; ++i) 86 { 87 if (!vis[i]) 88 { 89 vec.clear(); 90 vis[i] = true; 91 vec.push_back(val[i]); 92 for (int j = (i + k) % n; j != i && !vis[j]; j = (j + k) % n) 93 { 94 vis[j] = true; 95 vec.push_back(val[j]); 96 } 97 ans = max(ans, solve()); 98 } 99 } 100 if (ans >= s) ans = 0; 101 else ans = s - ans; 102 printf("Case #%d: %lld\n", cas, ans); 103 } 104 } 105 106 int main() 107 { 108 #ifdef LOCAL_JUDGE 109 freopen("Text.txt", "r", stdin); 110 #endif // LOCAL_JUDGE 111 112 RUN(); 113 114 #ifdef LOCAL_JUDGE 115 fclose(stdin); 116 #endif // LOCAL_JUDGE 117 118 return 0; 119 }
H - Search for Answer
留坑。
I - Tree and Permutation
题意:一个序列的值为第一个点走到后面n-1个点的距离和,求n!个序列的和
思路:对于每条边,这条边的左右端点均要走向对方。那么对于每条边的经过的次数为左右端点节点数的乘积。但是每个点都作为起点(n-1)!次,求和即可得到答案。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long ll; 6 const int INF = 0x3f3f3f3f; 7 const ll INFLL = 0x3f3f3f3f3f3f3f3f; 8 const int MOD = (int)1e9 + 7; 9 const int maxn = (int)1e5 + 10; 10 11 struct Edge { 12 int u, v; 13 ll w; 14 inline Edge() { } 15 inline Edge(int u,int v,ll w):u(u),v(v),w(w){} 16 }; 17 18 int n; 19 ll ans; 20 int son[maxn]; 21 vector<Edge>G[maxn]; 22 23 inline void Init() 24 { 25 ans = 0; 26 memset(son, 0, sizeof son); 27 for (int i = 1; i <= n; ++i) 28 { 29 G[i].clear(); 30 } 31 } 32 33 inline void DFS(int u, int pre) 34 { 35 son[u] = 1; 36 for (auto it : G[u]) 37 { 38 int v = it.v; 39 if (v == pre) continue; 40 DFS(v, u); 41 son[u] += son[v]; 42 ans = (ans + (ll)son[v] * (n - son[v]) % MOD * it.w) % MOD; 43 } 44 } 45 46 inline void RUN() 47 { 48 while (~scanf("%d", &n)) 49 { 50 Init(); 51 for (int i = 1; i < n; ++i) 52 { 53 int u, v; 54 ll w; 55 scanf("%d %d %lld", &u, &v, &w); 56 G[u].push_back(Edge(u, v, w)); 57 G[v].push_back(Edge(v, u, w)); 58 } 59 DFS(1, -1); 60 for (int i = 1; i <= n - 1; ++i) 61 { 62 ans = (ans * i) % MOD; 63 } 64 ans = (ans * 2) % MOD; 65 printf("%lld\n", ans); 66 } 67 } 68 69 int main() 70 { 71 #ifdef LOCAL_JUDGE 72 freopen("Text.txt", "r", stdin); 73 #endif // LOCAL_JUDGE 74 75 RUN(); 76 77 #ifdef LOCAL_JUDGE 78 fclose(stdin); 79 #endif // LOCAL_JUDGE 80 return 0; 81 }
J - YJJ's Salesman
题意:给出若干个点 ,范围属于(0, 0) - (1e9, 1e9) 从(0, 0) 走到(1e9, 1e9),只能向上走,向右走,或者右上角走,有些点有权值,不能回头,求最后获得的最大权值
思路:最大上升子序列的权值和 先按x排序,再对y离散化,线段树优化
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 6 int t, n, m; 7 int tmp[N]; 8 9 struct Data 10 { 11 int x, y, v; 12 inline void scan() 13 { 14 scanf("%d%d%d", &x, &y, &v); 15 } 16 inline bool operator < (const Data &r) const 17 { 18 return x < r.x || x == r.x && y < r.y; 19 } 20 }arr[N]; 21 22 inline void Init() 23 { 24 for (int i = 1; i <= n; ++i) tmp[i] = arr[i].y; 25 sort(tmp + 1, tmp + 1 + n); 26 m = unique(tmp + 1, tmp + 1 + n) - tmp - 1; 27 } 28 29 inline int Get(int x) 30 { 31 return lower_bound(tmp + 1, tmp + 1 + m, x) - tmp; 32 } 33 34 struct node 35 { 36 int l, r; 37 int Max; 38 inline node() {} 39 inline node(int l, int r, int Max) : l(l), r(r), Max(Max) {} 40 }tree[N << 2]; 41 42 inline void pushup(int id) 43 { 44 tree[id].Max = max(tree[id << 1].Max, tree[id << 1 | 1].Max); 45 } 46 47 inline void build(int id, int l, int r) 48 { 49 tree[id] = node(l, r, 0); 50 if (l == r) return; 51 int mid = (l + r) >> 1; 52 build(id << 1, l, mid); 53 build(id << 1 | 1, mid + 1, r); 54 } 55 56 inline void update(int id, int pos, int val) 57 { 58 if (tree[id].l == tree[id].r) 59 { 60 tree[id].Max = max(tree[id].Max, val); 61 return; 62 } 63 int mid = (tree[id].l + tree[id].r) >> 1; 64 if (pos <= mid) update(id << 1, pos, val); 65 else update(id << 1 | 1, pos, val); 66 pushup(id); 67 } 68 69 int ansMax; 70 71 inline void query(int id, int l, int r) 72 { 73 if (l > r) return; 74 if (tree[id].l >= l && tree[id].r <= r) 75 { 76 ansMax = max(ansMax, tree[id].Max); 77 return; 78 } 79 int mid = (tree[id].l + tree[id].r) >> 1; 80 if (l <= mid) query(id << 1, l, r); 81 if (r > mid) query(id << 1 | 1, l, r); 82 } 83 84 inline void Run() 85 { 86 scanf("%d", &t); 87 while (t--) 88 { 89 scanf("%d", &n); 90 for (int i = 1; i <= n; ++i) arr[i].scan(); 91 sort(arr + 1, arr + 1 + n); Init(); build(1, 1, n); 92 for (int i = 1; i <= n; ++i) arr[i].y = Get(arr[i].y); 93 vector <int> v; 94 int ans = arr[1].v; v.push_back(1); 95 for (int i = 2; i <= n; ++i) 96 { 97 if (arr[i].x != arr[i - 1].x) 98 { 99 for (auto it : v) 100 { 101 update(1, arr[it].y, arr[it].v); 102 } 103 v.clear(); 104 } 105 ansMax = 0; query(1, 1, arr[i].y - 1); 106 ans = max(ans, ansMax + arr[i].v); 107 //printf("%d %d %d\n", i, ansMax, arr[i].v); 108 arr[i].v += ansMax; 109 v.push_back(i); 110 } 111 printf("%d\n", ans); 112 } 113 } 114 115 int main() 116 { 117 #ifdef LOCAL 118 freopen("Test.in", "r", stdin); 119 #endif 120 121 Run(); 122 123 return 0; 124 }