【20191102】考试

mac2026-10-01  5

T1 最大K段和

考场上想了一个贪心:处理出所有仅含正数的极大区间。如果区间数量 ≤ m \leq m ≤m,直接把所有正数的和输出即可。否则,我们选取前 m m m大区间,进行以下操作使结果最大化: · 合并两个已选区间,尝试选取更多未选区间 · 合并一个已选区间和一个未选区间 实现比较繁琐,敬请参考代码:

#include<bits/stdc++.h> #define ll long long using namespace std; const int mn = 100005; struct node{ int l, r, id; ll sum; bool operator< (const node x) {return sum > x.sum;} }h[mn], tmp[mn]; int a[mn], cnt, cho[mn], tot; ll s[mn]; bool vis[mn]; int main() { int n, m; ll ans = 0; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++) scanf("%d", &a[i]), s[i] = s[i - 1] + 1ll * a[i]; for(int i = 1; i <= n; i++) if(a[i] >= 0) { h[++cnt] = (node) {i, i + 1, 0, a[i]}, h[cnt].id = cnt; while(h[cnt].r <= n && a[h[cnt].r] >= 0) h[cnt].sum += a[h[cnt].r], ++h[cnt].r; --h[cnt].r, i = h[cnt].r; } if(cnt <= m) { for(int i = 1; i <= cnt; i++) ans += h[i].sum; printf("%lld\n", ans); return 0; } for(int i = 1; i <= cnt; i++) tmp[i] = h[i]; sort(tmp + 1, tmp + 1 + cnt); for(int i = 1; i <= m; i++) ans += tmp[i].sum, vis[tmp[i].id] = 1, cho[++tot] = tmp[i].id; ++m; int las = 1; for(int i = 1; i <= cnt && m <= cnt; ) if(vis[i]) { bool flg = 0; for(int j = las + 1; j <= cnt; j++) if(vis[j]) { flg = 1; if(h[i].r < tmp[m].l && tmp[m].r < h[j].l) { ll del = max(s[h[j].l - 1] - s[tmp[m].r], s[tmp[m].l-1] - s[h[i].r]); if(del + tmp[m].sum >= 0) ans += del + tmp[m].sum, vis[tmp[m].id] = 1, cho[++tot] = tmp[m].id, i = (del == (s[tmp[m].l-1] - s[h[i].r]) ? tmp[m].id : j), ++m; } else if(tmp[m].sum + s[h[j].l-1] - s[h[i].r] >= 0) ans += tmp[m].sum + s[h[j].l-1] - s[h[i].r], vis[tmp[m].id] = 1, cho[++tot] = tmp[m].id, i = j, ++m; else i = j; if(i == j) las = j; else las = j - 1; break; } if(!flg) break; } else ++i; sort(cho + 1, cho + 1 + tot); for(int i = 1; i <= cnt; i++) if(!vis[i]) { int r = lower_bound(cho + 1, cho + tot + 1, i) - cho; if(r == tot) { ll del = s[h[i].l-1] - s[h[cho[r]].r]; if(del + h[i].sum >= 0) ans += del + h[i].sum, cho[r] = i; } int l = cho[r - 1], pos = r; r = cho[r]; ll del = max(s[h[r].l - 1] - s[h[i].r], s[h[i].l-1] - s[h[l].r]); if(del + h[i].sum >= 0) { ans += del + h[i].sum; if(del == s[h[i].l-1] - s[h[l].r]) cho[pos] = i; else i = pos; } } printf("%lld\n", ans); }

T2

就是对原序列镜像以后求LIS

#include<bits/stdc++.h> using namespace std; const int mn = 100005; int a[mn << 1], f[mn << 1]; int c[mn], num[mn], cnt; inline void update(int p, int v) {while(p <= cnt) c[p] = max(c[p], v), p += p & -p;} inline int getmax(int p) { int ret = 0; while(p) ret = max(ret, c[p]), p -= p & -p; return ret; } int main() { //freopen("dequexlis.in", "r", stdin); //freopen("dequexlis.out","w",stdout); int n; scanf("%d", &n); for(int i = n + 1; i <= (n << 1); i++) scanf("%d", &a[i]), a[(n << 1) - i + 1] = a[i], num[++cnt] = a[i]; n <<= 1, sort(num + 1, num + 1 + cnt), cnt = unique(num + 1, num + 1 + cnt) - num - 1; for(int i = 1; i <= n; i++) a[i] = lower_bound(num + 1, num + 1 + cnt, a[i]) - num; for(int i = 1; i <= n; i++) f[i] = getmax(a[i] - 1) + 1, update(a[i], f[i]); printf("%d\n", *max_element(f + 1, f + 1 + n)); }

T3 最大前缀和

机房dalao想出了 O ( n ) O(n) O(n)方法。 我们把 + 1 +1 +1、 − 1 -1 −1看成在坐标系中走路,+1为往右上方走,-1往右下方走(如图) 那么原问题就相当于从图中的(0,0)走到(n+m,n-m)。由题意不难得出,走过去的方案数为 C n + m n \Large C_{n+m}^n Cn+mn​。 原问题要求我们求所有前缀最大值之和。不妨枚举这个最大值 i i i(显然 i ∈ [ n − m , n ] i \in [n-m, n] i∈[n−m,n]),然后考虑计算每一个最大值的贡献。 枚举了 i i i后,就是要求恰好过 y = i y=i y=i的折线数目。不妨先求不超过 y = i y=i y=i的折线数,那么就可以用总方案数-不合法数计算,即 C n + m n − C n + m m + i + 1 \Large C_{n+m}^n-C_{n+m}^{m+i+1} Cn+mn​−Cn+mm+i+1​。之后再减去在 y = i − 1 y=i-1 y=i−1及以下的数目即可。

#include<bits/stdc++.h> #define ll long long using namespace std; const int mn = 8005, mod = 998244853; ll frac[mn], inv[mn]; inline void init(int n) { frac[0] = 1; for(int i = 1; i <= n; i++) (frac[i] = frac[i-1] * 1ll * i) %= mod; inv[0] = inv[1] = 1; for(int i = 2; i <= n; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod; for(int i = 2; i <= n; i++) (inv[i] *= inv[i-1]) %= mod; } inline ll C(int n, int m) {return n < m ? 0 : frac[n] * inv[m] % mod * inv[n - m] % mod;} int main() { int n, m; scanf("%d%d", &n, &m), init(n + m); ll ans = 0, las = 0; for(int i = n - m; i <= n; i++) { ll tmp = (((C(n + m, n) - C(n + m, m + i + 1)) % mod - las) % mod + mod) % mod; (las += tmp) %= mod; if(i > 0) (ans += tmp * 1ll * i) %= mod; } printf("%lld\n", ans); }
最新回复(0)