2017年浙江中医药大学程序设计竞赛 Solution
A:
树剖板子题
求最小值的时候要注意值是不是有负数,如果有,初值要置为$-INF$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 30010 5 #define INF 0x3f3f3f3f 6 int n, q, arr[N]; 7 vector <int> G[N]; 8 9 int fa[N], deep[N], sze[N], son[N], top[N], p[N], fp[N], cnt; 10 void DFS(int u) 11 { 12 sze[u] = 1; 13 for (auto v : G[u]) if (v != fa[u]) 14 { 15 fa[v] = u; 16 deep[v] = deep[u] + 1; 17 DFS(v); 18 sze[u] += sze[v]; 19 if (!son[u] || sze[v] > sze[son[u]]) son[u] = v; 20 } 21 } 22 23 void getpos(int u, int sp) 24 { 25 top[u] = sp; 26 p[u] = ++cnt; 27 fp[cnt] = u; 28 if (!son[u]) return; 29 getpos(son[u], sp); 30 for (auto v : G[u]) if (v != fa[u] && v != son[u]) 31 getpos(v, v); 32 } 33 34 namespace SEG 35 { 36 struct node 37 { 38 int Max, sum; 39 node () {} 40 node (int Max, int sum) : Max(Max), sum(sum) {} 41 node operator + (const node &other) const { return node(max(Max, other.Max), sum + other.sum); } 42 }a[N << 2], res; 43 void build(int id, int l, int r) 44 { 45 if (l == r) 46 { 47 a[id] = node(arr[fp[l]], arr[fp[l]]); 48 return; 49 } 50 int mid = (l + r) >> 1; 51 build(id << 1, l, mid); 52 build(id << 1 | 1, mid + 1, r); 53 a[id] = a[id << 1] + a[id << 1 | 1]; 54 } 55 void update(int id, int l, int r, int pos, int val) 56 { 57 if (l == r) 58 { 59 a[id] = node(val, val); 60 return; 61 } 62 int mid = (l + r) >> 1; 63 if (pos <= mid) update(id << 1, l, mid, pos, val); 64 else update(id << 1 | 1, mid + 1, r, pos, val); 65 a[id] = a[id << 1] + a[id << 1 | 1]; 66 } 67 void query(int id, int l, int r, int ql, int qr) 68 { 69 if (l >= ql && r <= qr) 70 { 71 res = res + a[id]; 72 return; 73 } 74 int mid = (l + r) >> 1; 75 if (ql <= mid) query(id << 1, l, mid, ql, qr); 76 if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr); 77 } 78 } 79 80 void query(int u, int v) 81 { 82 while (top[u] != top[v]) 83 { 84 if (deep[top[u]] < deep[top[v]]) swap(u, v); 85 SEG::query(1, 1, n, p[top[u]], p[u]); 86 u = fa[top[u]]; 87 } 88 if (deep[u] > deep[v]) swap(u, v); 89 SEG::query(1, 1, n, p[u], p[v]); 90 } 91 92 int main() 93 { 94 while (scanf("%d%d", &n, &q) != EOF) 95 { 96 cnt = 0; 97 memset(son, 0, sizeof son); 98 for (int i = 1; i <= n; ++i) scanf("%d", arr + i), G[i].clear(); 99 for (int i = 1, u, v; i < n; ++i) 100 { 101 scanf("%d%d", &u, &v); 102 G[u].push_back(v); 103 G[v].push_back(u); 104 } 105 DFS(1); getpos(1, 1); SEG::build(1, 1, n); 106 for (int i = 1, op, x, y; i <= q; ++i) 107 { 108 scanf("%d%d%d", &op, &x, &y); 109 if (op == 2) SEG::update(1, 1, n, p[x], y); 110 else 111 { 112 SEG::res = SEG::node(-INF, 0); 113 query(x, y); 114 printf("%d\n", op == 0 ? SEG::res.Max : SEG::res.sum); 115 } 116 } 117 } 118 return 0; 119 }
B:
$b^2 = 2 \cdot a \cdot (a + 1) ^ 2$
$b = \sqrt{2 \cdot a} \cdot (a +1)$
所以a一定是个平方数的两倍
即$a = 2 \cdot i^2 $
枚举出答案,再二分查找
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ull unsigned long long 5 #define N 6000020 6 ull res[N]; 7 8 int main() 9 { 10 for (int i = 0; i < N; ++i) 11 res[i] = (ull)2 * i * ((ull)2 * i * i + 1); 12 //cout << res[N - 1] << endl; 13 int t; cin >> t; 14 while (t--) 15 { 16 ull n; scanf("%llu", &n); 17 int pos = lower_bound(res, res + N, n) - res; 18 printf("%llu\n", res[pos]); 19 } 20 return 0; 21 }
C:
$倒着推过来$
$对于'u' 记录一下最近的位置$
$对于'm',记录一下离他最近的'u'的位置$
$对于'c' , 记录一下离(离他最近的'm'的)最近的'z'的位置$
$对于'z',可以直接求答案$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 100010 5 #define INF 0x3f3f3f3f 6 char s[N]; 7 int f[4]; 8 9 int main() 10 { 11 while (scanf("%s", s + 1) != EOF) 12 { 13 memset(f, INF, sizeof f); 14 for (int i = strlen(s + 1); i >= 1; --i) 15 { 16 if (s[i] == 'u') f[3] = i; 17 else if (s[i] == 'm') 18 { 19 if (f[3] != INF) 20 f[2] = min(f[2], f[3] - 1); 21 } 22 else if (s[i] == 'c') 23 { 24 if (f[2] != INF) 25 f[1] = min(f[1], f[2] - 1); 26 } 27 else if (s[i] == 'z') 28 { 29 if (f[1] != INF) 30 f[0] = min(f[0], f[1] - i - 1); 31 } 32 } 33 printf("%d\n", f[0] == INF ? -1 : f[0]); 34 } 35 return 0; 36 }
D:
贪心
考虑所有物品都要放进去
那么肯定先放$a_i <= b_i的物品,因为这些物品放进去背包会扩容$
那么放这些物品的时候按照$a_i从小到大的顺序排放$
$因为如果小的都放不下,那么大的肯定放不下,但是大的可以等小的放进去扩容之后可能就可以放下$
那么剩下的物品按照$(a_i - b_i)从小到大的顺序排放$
$因为我们要放一件物品要尽量使得背包缩小的体积较小,这样对后面物品的影响也较小$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 100010 6 int t, n; ll v; 7 struct node 8 { 9 int a, b; 10 node () {} 11 node (int a, int b) : a(a), b(b) {} 12 }; 13 vector <node> p, q; 14 15 bool ok() 16 { 17 for (auto it : p) 18 { 19 if (v < it.a) return false; 20 v += it.b - it.a; 21 } 22 for (auto it : q) 23 { 24 if (v < it.a) return false; 25 v += it.b - it.a; 26 } 27 return true; 28 } 29 30 int main() 31 { 32 scanf("%d", &t); 33 while (t--) 34 { 35 scanf("%d%lld", &n, &v); 36 p.clear(), q.clear(); 37 for (int i = 1, a, b; i <= n; ++i) 38 { 39 scanf("%d%d", &a, &b); 40 if (a <= b) p.emplace_back(a, b); 41 else q.emplace_back(a, b); 42 } 43 sort(p.begin(), p.end(), [](node x, node y) { return x.a < y.a; }); 44 sort(q.begin(), q.end(), [](node x, node y) { return x.a - x.b > y.a - y.b; }); 45 puts(ok() ? "yes" : "no"); 46 } 47 return 0; 48 }
E:
签到。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 int t; 6 ll n, k; 7 8 int main() 9 { 10 scanf("%d", &t); 11 while (t--) 12 { 13 scanf("%lld%lld", &n, &k); 14 printf("%lld\n", k); 15 } 16 return 0; 17 }
F:
题意:
给出一个循环节,求这个循环节上的哪些位置,使得从这个位置出发
不论到达哪个位置,前缀1的个数都大于前缀0的个数
思路:
刚开始的想法是线段树的$O(nlogn)做法$
维护处前缀0的个数和前缀1的个数,然后枚举起始位置,两个起始位置之间$O(logn)转移,T了$
然后又考虑了单调队列的做法,$维护一个lazy, O(n),过了$
最后看了看别人的代码,答案是$1的个数-0的个数,晕了$
感觉,首先复杂度没有算法,很明显的需要线性做法,却要硬刚带$log的$
再考虑一下,答案为什么是$1的个数-0的个数$
首先,所有0所处的位置都不可能作为起始位置
再考虑,有哪些1是不可以的
我们考虑连续的0,如果有一段连续的$x个0,那么这x个0往前数x个1,这x个1都是不可以的$
因为肯定至少存在一个位置不满足要求
$再考虑离散的0,那么离散的0直接理解为连续的1个0即可$
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 200010 5 int t, n, a[N], sum[N], dq[N]; 6 7 int main() 8 { 9 scanf("%d", &t); 10 while (t--) 11 { 12 scanf("%d", &n); 13 for (int i = 1; i <= n; ++i) 14 { 15 scanf("%d", a + i); 16 a[i + n] = a[i]; 17 } 18 int l = 1, r = 0; 19 for (int i = 1, tmp = 0; i <= n; ++i) 20 { 21 tmp += a[i]; 22 sum[i] = 2 * tmp - i; 23 while (l <= r && sum[dq[r]] > sum[i]) --r; 24 dq[++r] = i; 25 } 26 int res = 0, lazy = 0; 27 for (int i = 1; i <= n; ++i) 28 { 29 while (l <= r && dq[l] < i) ++l; 30 if (sum[dq[l]] + lazy > 0) ++res; 31 sum[i + n] = sum[i + n - 1] + 1 * (a[i + n] ? 1 : -1); 32 while (l <= r && sum[dq[r]] > sum[i + n]) --r; 33 lazy += 1 * (a[i] ? -1 : 1); 34 dq[++r] = i + n; 35 } 36 printf("%d\n", res); 37 } 38 return 0; 39 }
G:
签到。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main() 5 { 6 int t; cin >> t; 7 while (t--) 8 { 9 long long n, m; 10 scanf("%lld%lld", &n, &m); 11 printf("%lld\n", m - n); 12 } 13 return 0; 14 }
H:
题意:
将一个n * n矩形分成两个相同的部分,求方案数
思路:
n 为奇数 答案为0
然后从中间开始搜,对称标记
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int t, ans[11], res, n; 5 int used[20][20]; 6 int Move[][2] = 7 { 8 0, -1, 9 0, 1, 10 1, 0, 11 -1, 0, 12 }; 13 14 void DFS(int x, int y) 15 { 16 if (x == 0 || y == 0 || x == n || y == n) 17 { 18 ++res; 19 return; 20 } 21 for (int i = 0; i < 4; ++i) 22 { 23 int nx = x + Move[i][0]; 24 int ny = y + Move[i][1]; 25 if (used[nx][ny] == 0) 26 { 27 used[nx][ny] = 1; 28 used[n - nx][n - ny] = 1; 29 DFS(nx, ny); 30 used[nx][ny] = 0; 31 used[n - nx][n - ny] = 0; 32 } 33 } 34 } 35 36 37 int main() 38 { 39 for (int i = 1; i < 10; ++i) 40 { 41 if (i & 1) ans[i] = 0; 42 else 43 { 44 memset(used, 0, sizeof used); 45 res = 0; n = i; 46 used[i / 2][i / 2] = 1; 47 DFS(i / 2, i / 2); 48 ans[i] = res / 4; 49 } 50 } 51 scanf("%d", &t); 52 while (t--) 53 { 54 scanf("%d", &n); 55 printf("%d\n", ans[n]); 56 } 57 return 0; 58 }
I:
按题意模拟即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define N 10010 5 int t, n; 6 string str; 7 string var[N], save[N]; 8 int Size[N], Len[N]; 9 map <string, int> mp; 10 11 void getName(int i, int len) 12 { 13 string name = ""; 14 for (; i < len; ++i) 15 { 16 if (str[i] == '[') 17 { 18 ++i; 19 break; 20 } 21 name += str[i]; 22 } 23 int big = 0; 24 for (; i < len; ++i) 25 { 26 if (str[i] == ']') break; 27 big = big * 10 + str[i] - '0'; 28 } 29 ++n; 30 mp[name] = n; 31 var[n] = name; 32 save[n] = ""; 33 Size[n] = big; 34 Len[n] = 0; 35 } 36 37 int main() 38 { 39 ios::sync_with_stdio(false); 40 cin.tie(0); cout.tie(0); 41 cin >> t; getline(cin, str); 42 while (t--) 43 { 44 n = 0; 45 mp.clear(); 46 while (1) 47 { 48 getline(cin, str); 49 if (str == "return 0;") break; 50 if (str[0] == 'c' && str[1] == 'h') 51 { 52 for (int i = 0, len = str.size(); i < len; ++i) if (str[i] == ' ') 53 getName(i + 1, len); 54 } 55 else if (str[0] == 'g') 56 { 57 string name = ""; 58 str += "\n"; 59 int i = 0, len = str.size(); 60 for (; i < len; ++i) if (str[i] == ' ') 61 { 62 ++i; 63 break; 64 } 65 for (; i < len; ++i) 66 { 67 if (str[i] == ' ') 68 { 69 ++i; 70 break; 71 } 72 name += str[i]; 73 } 74 int id = mp[name]; 75 Len[id] = len - i; 76 string s = ""; 77 for (int j = 0; i < len && j < Size[id]; ++i, ++j) 78 s += str[i]; 79 save[id] = s; 80 } 81 else 82 { 83 string name = ""; 84 int i = 0, len = str.size(); 85 for (; i < len; ++i) if (str[i] == ' ') 86 { 87 ++i; 88 break; 89 } 90 for (; i < len; ++i) name += str[i]; 91 int id = mp[name]; 92 string res = save[id]; 93 int remind = Len[id] - Size[id]; 94 for (int i = id + 1; remind > 0 && i <= n; ++i) 95 { 96 res += save[i]; 97 remind -= Size[i] - Len[i]; 98 } 99 if (res.end()[-1] != '\n') res += "\n"; 100 cout << res; 101 } 102 } 103 } 104 return 0; 105 }
j:
考虑如果没有$gcd(x, y) != 1 的限制$
那么直接处理处b[a[i]]
再对于$a[b[i]] 直接求答案$
有这个限制我们可以考虑容斥,可以直接用莫比乌斯函数来容斥
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define ll long long 5 #define N 100010 6 int n; 7 int a[N], b[N]; 8 bool check[N]; int mu[N], prime[N]; 9 void Moblus() 10 { 11 memset(check, false, sizeof check); 12 mu[1] = 1; 13 int tot = 0; 14 for (int i = 2; i < N; ++i) 15 { 16 if (!check[i]) 17 { 18 prime[++tot] = i; 19 mu[i] = -1; 20 } 21 for (int j = 1; j <= tot; ++j) 22 { 23 if (i * prime[j] >= N) break; 24 check[i * prime[j]] = true; 25 if (i % prime[j] == 0) 26 { 27 mu[i * prime[j]] = 0; 28 break; 29 } 30 else 31 { 32 mu[i * prime[j]] = -mu[i]; 33 } 34 } 35 } 36 } 37 38 ll vis[N]; 39 ll f(int t) 40 { 41 ll res = 0; 42 for (int i = t; i <= n; i += t) ++vis[b[a[i]]]; 43 for (int i = t; i <= n; i += t) res += vis[a[b[i]]]; 44 for (int i = t; i <= n; i += t) --vis[b[a[i]]]; 45 return res; 46 } 47 48 int main() 49 { 50 Moblus(); 51 while (scanf("%d", &n) != EOF) 52 { 53 memset(vis, 0, sizeof vis); 54 for (int i = 1; i <= n; ++i) scanf("%d", a + i); 55 for (int i = 1; i <= n; ++i) scanf("%d", b + i); 56 ll res = 0; 57 for (int i = 1; i <= n; ++i) 58 res += mu[i] * f(i); 59 printf("%lld\n", res); 60 } 61 return 0; 62 }