HOI R1 Overall
Register_int · · 题解
A
首先有个比较显然的 dp。设
含义是枚举拿不拿
上个线段树即可,复杂度
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 10;
struct node {
int l, r; ll v, add;
} t[MAXN << 2];
inline
void upd(int p, ll v) {
t[p].add += v, t[p].v += v;
}
inline
void pushup(int p) {
t[p].v = max(t[p << 1].v, t[p << 1 | 1].v);
}
inline
void pushdown(int p) {
if (!t[p].add) return ;
upd(p << 1, t[p].add), upd(p << 1 | 1, t[p].add), t[p].add = 0;
}
void build(int l, int r, int p) {
if (l > r) return ;
t[p].l = l, t[p].r = r, t[p].v = t[p].add = 0;
if (l == r) return ; int mid = l + r >> 1;
build(l, mid, p << 1), build(mid + 1, r, p << 1 | 1);
}
void modify(int l, int r, ll v, int p) {
if (l > r) return ;
if (l <= t[p].l && t[p].r <= r) return upd(p, v);
pushdown(p); int mid = t[p].l + t[p].r >> 1;
if (l <= mid) modify(l, r, v, p << 1);
if (r > mid) modify(l, r, v, p << 1 | 1); pushup(p);
}
ll query(int l, int r, int p) {
if (l > r) return 0;
if (l <= t[p].l && t[p].r <= r) return t[p].v;
pushdown(p); int mid = t[p].l + t[p].r >> 1; ll res = 0;
if (l <= mid) res = max(res, query(l, r, p << 1));
if (r > mid) res = max(res, query(l, r, p << 1 | 1)); return res;
}
int T, n; ll a[MAXN], dp[MAXN];
int main() {
for (scanf("%d", &T); T--;) {
scanf("%d", &n), build(1, n, 1);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = n, l, r; i; i--) {
dp[i] = a[i], l = i + 1, r = min<ll>(i + a[i], n);
dp[i] = max(dp[i], query(l, r, 1));
modify(l, n, a[i], 1), modify(i, i, dp[i], 1);
}
printf("%lld\n", dp[1]);
}
}
Bonus:如何做到
B
放 B 纯粹是怕 A 放分讨会被骂。
首先判掉一些显然有/无解的情况,比如初始就有环或者
本题关键在于一个交换操作。若一个空位填要填的数会导致成环,那么将其与其它空位交换即可消环。这个是好理解的,否则说明之前就存在环。
此时可以直接取一个大质数,在该质数的模意义下求解即可。除法可以直接转成逆元。
可以发现我们并不关心约分后的结果,只希望找到一个映射,使得映射后的答案 便于计算 且 能对应到原式的唯一解。还是考虑取一个大质数。设大质数为
其中,
可以用 BSGS 解决。取一个根号级别的
对于情况一直接取最小值,对于情况二需要排序二分。当然都是容易的。大整数乘法可以 __int128 草过去,复杂度为
#include <bits/stdc++.h>
using namespace std;
// using fread
#define INPUT_OPTIMIZE
// using fwrite
//#define OUTPUT_OPTIMIZE
namespace IO {
// by R_i.
}
using namespace IO;
typedef long long ll;
typedef __int128 lll;
typedef pair<lll, lll> plll;
const int MAXN = 6e4 + 10, B = 6e4;
const lll mod = 9000000000000000041ll;
inline
lll qpow(lll b, lll p) {
lll res = 1;
for (; p; p >>= 1, b = b * b % mod) if (p & 1) res = res * b % mod;
return res;
}
int n, m; ll tmp; lll p = 1, q = 1, x, y, z;
plll a[MAXN];
int main() {
read(n, m, tmp);
for (int i = 1; i <= n; i++) read(tmp), p = p * tmp % mod;
for (int i = 1; i <= m; i++) read(tmp), q = q * tmp % mod;
p = p * qpow(q, mod - 2) % mod, x = p;
for (int i = 1; i < B; i++) a[i] = plll(x, i), x = (x + p) % mod;
sort(a, a + B), y = p;
for (ll i = 1, j; ; i += B) {
if (y <= 3e9) return printf("%lld %lld", (ll)y, i), 0;
j = lower_bound(a, a + B, plll(mod - y + 1, 0)) - a;
if (j < B) {
z = a[j].first + y - mod;
if (z <= 3e9) return printf("%lld %lld", (ll)z, (ll)a[j].second + i), 0;
}
y = (y + x) % mod;
}
}
D
放 D 纯粹是怕前三题放交互会被骂。
可以通过表现来判断是否是
- 与
\verb!Aa! 杂交,相等概率出现\verb!Aa,aa! ,即\frac12 概率表现为\verb!a! 。 - 与
\verb!AA! 杂交,只会出现\verb!Aa! ,永远表现为\verb!A! 。
于是我们可以用
但是没有
- 两个基因型(我们称为亲代)杂交
k_2 次,得到k_2 个基因型(我们称为子代)。每个子代再分别与两个亲代各杂交一次。
如果子代里已经有了
由于对于每组两个基因型,我们需要操作
在题目的限制下,
有这样一个小优化。在该方法的过程中,判断
然后这个做法就直接对了。为什么呢?因为这个方法是不会将
最后就是调参了。取
#include <bits/stdc++.h>
using namespace std;
char query(int k);
void cross(int i, int j);
vector<string> guess(int n) {
int p = 0, tot = n;
vector<char> a(n);
vector<string> ans(n);
for (int i = 1; i <= n; i++) a[i - 1] = query(i);
for (int i = 0; i < n; i++) if (islower(a[i])) ans[i] = "aa", p = i + 1;
if (!p) {
int pre = 0, f = 0;
for (int i = 2; i <= n; i += 2) {
for (int j = 0; j < 15; j++) cross(i - 1, i), a.push_back(query(++tot));
f = 0;
for (int j = 1; j <= 15; j++) if (islower(a[tot - j])) { f = 1, p = tot - j + 1; break; }
if (f) { ans[i - 2] = ans[i - 1] = "Aa"; break; }
pre = tot, ans[i - 2] = ans[i - 1] = "AA";
for (int j = 0; j < 15; j++) cross(i - 1, pre - j), a.push_back(query(++tot));
for (int j = 1; j <= 15; j++) if (islower(a[tot - j])) { f = 1; p = tot - j + 1; break; }
if (f) { ans[i - 2] = "Aa"; break; }
for (int j = 0; j < 15; j++) cross(i, pre - j), a.push_back(query(++tot));
for (int j = 1; j <= 15; j++) if (islower(a[tot - j])) { f = 1; p = tot - j + 1; break; }
if (f) { ans[i - 1] = "Aa"; break; }
}
}
for (int i = 1; i <= n; i++) {
if (ans[i - 1] != "") continue;
for (int j = 0; j < 22; j++) {
cross(i, p);
if (islower(query(++tot))) { ans[i - 1] = "Aa"; break; }
}
if (ans[i - 1] == "") ans[i - 1] = "AA";
}
return ans;
}