1.23 高一期末总结
%%%Aurora!!! 向神真的强,每逢大考必AK!!!
话说这次考试不讲武德,看了半天发现没有dp?
T1:NKOJ5837 基因中有AC
题面:人类基因由碱基'A','C','G','T'构成。
何老板检查了一下自己的DNA,拿到了检查报告。
检测报告里有一段长度为N的由字母'A','C','G','T'构成的表示基因片段的字符串。
何老板发现,自己的基因中带了很多“AC”,众所周知他的口号是“Life is short,AC more!”,于是他想知道对于该基因片段中的任意一段区间[L,R]中,出现了多少次“AC”,请你快速帮他做出回答。
题解:我一拿到这个题就感觉我做过,一开始写了个前缀和结果写挂了,后面用树状数组,结果是我的端点处理错了???wtclwtcl。
#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; #define re register char ch[200005]; int c[200005]; int n, q; void modify(int x) { for (int i = x; i <= n; i += (i & -i)) c[i]++; } int query(int x) { int ans = 0; for (int i = x; i; i -= (i & -i)) ans += c[i]; return ans; } int main() { scanf("%d%d", &n, &q); scanf("%s", ch + 1); // if (ch[1] == 'A') mark[1] = 1; for (re int i = 2; i <= n; i++) { if (ch[i] == 'C' && ch[i - 1] == 'A') { modify(i); } } for (re int i = 1, l, r; i <= q; i++) { scanf("%d%d", &l, &r); printf("%d\n", query(r) - query(l)); } }
T2:NKOJ5811 奥特曼打怪
题面:
何老板最近在玩一款打怪兽的游戏,游戏虽然简单,他仍乐此不疲。
游戏中,何老板操控奥特曼与N只怪兽作战。每只怪兽都有一定的生命值,第i只怪兽的生命值为hi。
奥特曼可以发射光弹,何老板可以控制奥特曼往一只指定的怪兽发射光弹。怪兽被光弹击中后会损失A单位的生命值。该怪兽被击中的同时,光弹会发生爆炸,使得其他所有怪兽都损失B的生命值。
何老板想知道,奥特曼最少发射几次光弹,就能消灭所有怪兽(怪兽的生命值小于等于0,就被消灭了)。
题解:我是真的菜啊。这个题要二分发射光弹的次数然后验证,结果我直接以权值建了个堆硬跑,,,活该我挂了。
#include <bits/stdc++.h> using namespace std; #define ll long long ll n, a, b, h[1000005]; bool check(ll x) { ll cnt = 0; for (ll i = 1; i <= n; i++) { ll hh = h[i]; hh -= b * x; if (hh <= 0) continue; else cnt += ((hh - 1) / a + 1); if (cnt > x) return 0; } return 1; } //ll h[1000005]; main() { ll maxh = 0; scanf("%lld%d%d", &n, &a, &b); a -= b; for (ll i = 1; i <= n; i++) { scanf("%lld", &h[i]); maxh = max(maxh, h[i]); } ll l = 1, r = maxh, mid, ans; while (l <= r) { if (check(mid = l + r >> 1)) r = mid - 1, ans = mid; else l = mid + 1; } printf("%lld", ans); }
T3:NKOJ5832 老板砍树
题面:
何老板家门前有一排树,共 3 * N 棵 ,每棵树都有一定的高度,第i棵树高度为hi。
何老板需要添置家具,他雇你砍掉其中N棵树,剩下2*N棵。你可以砍掉其中任意N棵树,但何老板有个奇怪的要求,他希望剩下的树中,前N棵树的高度总和与后N棵树的高度总和之差,尽可能大。
请你计算出这个最大差值。
题解:这个题没什么好说的,把前n个数和后n个数分别用小根堆和大根堆存起来,然后中间n棵树用类似后悔堆的方法调整最优值即可。
#include <bits/stdc++.h> using namespace std; #define ll long long priority_queue <ll, vector<ll>, greater<ll> > P; priority_queue <ll> Q; ll a[300005], sum[300005], mini[300005]; main() { ll n; scanf("%lld", &n); for (ll i = 1; i <= 3 * n; i++) { scanf("%lld", &a[i]); } for (ll i = 1; i <= 2 * n; i++) { sum[i] = sum[i - 1]; if (P.size() < n) P.push(a[i]), sum[i] += a[i]; else if (a[i] > P.top()) { sum[i] -= P.top(), sum[i] += a[i]; P.pop(); P.push(a[i]); } } for (ll i = 3 * n; i > n; i--) { mini[i] = mini[i + 1]; if (Q.size() < n) Q.push(a[i]), mini[i] += a[i]; else if (a[i] < Q.top()) { mini[i] -= Q.top(), mini[i] += a[i]; Q.pop(); Q.push(a[i]); } } ll ans = -0x3f3f3f3f3f3f3f3f; for (ll i = n; i <= 2 * n; i++) { ans = max(ans, sum[i] - mini[i + 1]); } printf("%lld", ans); }
T4:NKOJ5810 平均身高
题面:
NK信竞队有N名队员。
因要参加一年一度的全校自编操比赛,何老板组织队员们进行排练。
N名队员站成一排,从左往右身高分别是a1,a2,...,aN。
何老板设计了一个炫酷的加分动作。需要选相邻的若干同学出来(连续一段同学)出来表演。但要求选出的这些同学的平均身高需要大于等于K。
何老板想知道,他总共有多少种选择方案。
概括:给出一个整数序列,需要从中选出一个连续子序列,使得该序列的平均值不小于k,求方案数。
题解:将所有人的身高各减去k,这个题就转化为了求一段子序列的和大于0,于是问题转化为了求顺序对。PS:此题规模较大,需要离散化。
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll n, k; ll c[1000005], a[1000005], sum[1000005]; void modify(ll x) { for (ll i = x; i <= n; i += (i & -i)) c[i]++; } ll query(ll x) { ll ans = 0; for (ll i = x; i; i -= (i & -i)) ans += c[i]; return ans; } main() { ll ans = 0; scanf("%lld%lld", &n, &k); for (ll i = 1; i <= n; i++) { scanf("%lld", &a[i]), a[i] -= k; } for (ll i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i]; ans += ((a[i] += a[i - 1]) >= 0); } sort(sum + 1, sum + n + 1); ll tot = unique(sum + 1, sum + n + 1) - sum - 1; for (ll i = 1; i <= n; i++) a[i] = lower_bound(sum + 1, sum + tot + 1, a[i]) - sum; for (ll i = 1; i <= n; i++) { ans += query(a[i]); modify(a[i]); } printf("%lld", ans); }
T5:NKOJ5831 作业积分
题面:
题解:真的淦,我直接贪难度大小能得50分hhh。其实是这么个思路,优先贪难度,记录题的种类,显然这k道题中每类题第一次出现的那道显然不会被替换,剩下的放在一个小根堆里,再讨论后面的题。如果后面的题出现了新的类型就替换,并且与当前最优值比较,于是就能保证最后得到的是最优值。
#include <bits/stdc++.h> using namespace std; typedef long long ll; bool mark[1000005]; struct node { ll a, w; bool operator< (node x) const { return w > x.w; } }e[1000005]; //inline bool cmp(node x, node y) {return x.w < y.w;} priority_queue <node> Q; main() { ll n, k; scanf("%lld%lld", &n, &k); for (ll i = 1; i <= n; i++) { scanf("%lld%lld", &e[i].a, &e[i].w); } sort(e + 1, e + n + 1); ll ans = 0, cnt = 0, sum = 0; for (ll i = 1; i <= n; i++) { if (i <= k) { sum += e[i].w; if (!mark[e[i].a]) cnt++, mark[e[i].a] = 1; else Q.push(e[i]); ans = max(ans, sum + cnt * cnt); } else { if (Q.empty()) break; if (mark[e[i].a]) continue; mark[e[i].a] = 1, cnt++; sum -= Q.top().w, Q.pop(); sum += e[i].w; ans = max(ans, sum + cnt * cnt); } } printf("%lld", ans); }
T6:NKOJ4922 猜球游戏
题面:
题解:hhh老板昨天告诉我们今天必考最小生成树,我看前五道题与最小生成树毫无关联,就想这个题是不是,没想到还真干出来了。
首先我们知道第0个杯子下没有小球,又有性质:知道[A,B-1]和[A,C]的奇偶性就能知道[B,C]的奇偶性,于是询问[L,R]的奇偶性实际上就是给L-1和R连一条权值为cij的边,最终只需要让所有点与0号店相连即可。所以这个题可以跑最小生成树。
#include <bits/stdc++.h> using namespace std; struct node { int u, v, w; bool operator< (node x) const { return w < x.w; } }e[4000005]; int tot, sum; int n; inline void add_e(int x, int y, int z) {e[++tot].u = x; e[tot].v = y; e[tot].w = z;} int fa[4000005]; int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);} void init() { for (int i = 1; i <= n; i++) fa[i] = i; } void Kruskal() { init(); int cnt = 0; for (int i = 1; i <= tot; i++) { int fx = find(e[i].u), fy = find(e[i].v); if (fx == fy) continue; fa[fx] = fy; sum += e[i].w; if (++cnt == n) break; } printf("%d", sum); } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = i, x; j <= n; j++) { scanf("%d", &x); add_e(i - 1, j, x); } } sort(e + 1, e + tot + 1); Kruskal(); }

浙公网安备 33010602011771号