| 题目 | A | B | C | D | E | F | G | H | I | J | K | L |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Solved | ♠\spadesuit♠ | ∙\bullet∙ | ♠\spadesuit♠ | ♠\spadesuit♠ | ∙\bullet∙ | ♠\spadesuit♠ | ∙\bullet∙ | ♠\spadesuit♠ |
♠\spadesuit♠比赛时通过;∙\bullet∙:赛后通过;⊘\oslash⊘ :比赛时尝试了未通过;∘\circ∘:比赛时未尝试
A. August
开场签到题,题意就是求题目中的图形面积,一眼看下去,直觉爱心的下半部分可以构成一个长方形。试了下答案对了。瞎搞才是硬道理
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 2e3 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1);
int main() {
#ifdef _DEBUG
// FILE* _INPUT = freopen("input.txt", "r", stdin);
// FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
int T;
rd(T);
while (T--) {
ll a, b; rd(a), rd(b);
ll res1 = a * b * 4;
ll res2 = a * a;
double sum = res1 + pi * res2;
printf("%.8f\n", sum);
}
return 0;
}
B. Bills of Paradise
这题一看就是一道线段树,但是训练的时候太困了实在没有复杂度低的想法。之后看了博客才恍然大悟,只要结合并查集就可以大大降低这题的复杂度(反正训练的时候就是想不到的呜呜)。
题意比较复杂,下面慢慢解释。
题目给定一个函数,用其产生nnn个数,存入数组aaa。
接下来给出qqq个询问。
- 询问FFF,输入一个数xxx,输出数组aaa中大于等于xxx的第一个未被标记的数,若没有则输出100000000000010000000000001000000000000。
- 询问DDD,输入一个数xxx,标记数组aaa中大于等于xxx的第一个未被标记的数,若没有则跳过这步操作。
- 询问CCC,输入一个数xxx,查询数组aaa中小于等于xxx的所有未标记数之和,若没有则输出0。
- 询问RRR,输入一个数xxx,清楚数组aaa中小于等于xxx的数的所有标记,若没有标记过,则不操作。(题目中提到这个操作总共只会进行10次)
这里是一些前置操作
- 用题目中给的函数进行aaa的生成后,将其sortsortsort从小到大排序。
- 维护数组aaa的前缀和。
- 初始化并查集。并查集维护这个数是否被标记过,若没有被标记过其祖先就是自己本身。
- 接下来进行建树,树的初始值为000,因此不需要pushuppushuppushup的操作,都赋值为0即可。树中保存的是标记的过的数的值之和,因此没被标记过的数在线段树中的权值也就自然是000了。
接下来是四种询问的具体细分
- 询问FFF,先使用lower_boundlower\_boundlower_bound二分查找到大于等于xxx的数,然后通过并查集查找其祖先,也就是没有被标记过的哪个数。若找到则直接输出这个数,没有找到就输出100000000000010000000000001000000000000。没有找到共有两种情况:第一种情况是就找不到一个数是大于等于xxx的,也就是lower_boundlower\_boundlower_bound出来的下标等于n+1n+1n+1(具体怎么lower_boundlower\_boundlower_bound这一点在代码中有体现);第二种情况是大于等于xxx的数每个都被标记过了,也就是他们的祖先是n+1n+1n+1。
- 询问DDD,同样使用lower_boundlower\_boundlower_bound二分查找到大于等于xxx的数,若数组aaa中能找到这样的数则继续进行操作。若其本身就没有被标记过,则直接标记其本身(线段树中进行updateupdateupdate),并将其祖先设置为他的后一个数。若其本身被标记过并且其祖先是n+1n+1n+1,说明找不到没有标记过的数了,则跳过此次操作。如果其祖先不是n+1n+1n+1,则将其祖先进行标记(线段树中进行updateupdateupdate),并且将其祖先的祖先设置为其祖先的后面一个数。
- 询问CCC,先使用upper_boundupper\_boundupper_bound二分查找到小于等于xxx的数,若找不到,也就是找到的则输出0。找到了就将算好的前缀和减去前面被标记过的数之和(使用线段树的queryqueryquery求和)就可以得到答案输出了。
- 询问RRR,同样使用upper_boundupper\_boundupper_bound二分查找到小于等于xxx的数,若找不到,就不操作了。找到了就updateupdateupdate清空区间内的所有值并将这个区间的祖先都还原为本身即可。
具体细节看代码,记得要开longlonglonglonglonglong
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#define lowbit(x) ((x) & -(x))
#define endl "\n"
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6 + 10, NN = 2e3 + 10, INF = 0x3f3f3f3f, LEN = 20;
const ll MOD = 1e9 + 7;
const ull seed = 31;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int n;
int fa[N];
ll a[N], sum[N], tree[N << 2], ERROR;
ull k1, k2;
ull xorShift128Plus() {
ull k3 = k1, k4 = k2;
k1 = k4;
k3 ^= k3 << 23;
k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
return k2 + k4;
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void merge(int x, int y) {
int fx = find(x), fy = find(y);
fa[fx] = fy;
}
void gen() {
scanf("%d %llu %llu", &n, &k1, &k2);
for (int i = 1; i <= n; i++)
a[i] = xorShift128Plus() % 999999999999 + 1;
}
inline void pushup(int rt) {
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
void buildtree(int rt, int l, int r) {
tree[rt] = 0;
if (l == r)
return;
int mid = (l + r) >> 1;
buildtree(rt << 1, l, mid);
buildtree(rt << 1 | 1, mid + 1, r);
}
void update(int rt, int l, int r, int id, ll val) {
if (l == r && l == id) {
tree[rt] += val;
return;
}
int mid = (l + r) >> 1;
if (id <= mid)
update(rt << 1, l, mid, id, val);
else
update(rt << 1 | 1, mid + 1, r, id, val);
pushup(rt);
}
void update_clear(int rt, int l, int r, int ql, int qr) {
if (l == r) {
tree[rt] = 0;
return;
}
int mid = (l + r) >> 1;
if (ql <= mid)
update_clear(rt << 1, l, mid, ql, qr);
if (qr > mid)
update_clear(rt << 1 | 1, mid + 1, r, ql, qr);
pushup(rt);
}
ll query(int rt, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr)
return tree[rt];
int mid = (l + r) >> 1;
ll ans = 0;
if (ql <= mid)
ans += query(rt << 1, l, mid, ql, qr);
if (qr > mid)
ans += query(rt << 1 | 1, mid + 1, r, ql, qr);
return ans;
}
void init() {
ERROR = 1e12;
sum[0] = 0;
for (int i = 1; i <= n; i++) {
fa[i] = i;
sum[i] = sum[i - 1] + a[i];
}
fa[n + 1] = n + 1;
}
int main() {
// ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
gen();
sort(a + 1, a + 1 + n);
init();
buildtree(1, 1, n);
int q;
scanf("%d", &q);
while (q--) {
char op;
ll x;
scanf(" %c%lld", &op, &x);
if (op == 'F') {
int id = lower_bound(a + 1, a + 1 + n, x) - a;
if (id == n + 1) {
printf("%lld\n", ERROR);
continue;
}
int pos = find(id);
if (pos == n + 1)
printf("%lld\n", ERROR);
else
printf("%lld\n", a[pos]);
} else if (op == 'D') {
int id = lower_bound(a + 1, a + 1 + n, x) - a;
if (id == n + 1)
continue;
if (id == find(id)) { // 未被标记
update(1, 1, n, id, a[id]);
merge(id, id + 1);
} else {
int pos = find(id + 1);
if (pos == n + 1)
continue;
update(1, 1, n, pos, a[pos]);
merge(pos, pos + 1);
}
} else if (op == 'C') {
int id = upper_bound(a + 1, a + 1 + n, x) - a - 1;
if (id == 0) {
printf("0\n");
continue;
}
ll ans = query(1, 1, n, 1, id);
ll temp = sum[id];
printf("%lld\n", temp - ans);
} else if (op == 'R') {
int id = upper_bound(a + 1, a + 1 + n, x) - a - 1;
if (id == 0)
continue;
update_clear(1, 1, n, 1, id);
for (int i = 1; i <= id; i++)
fa[i] = i;
}
}
return 0;
}
C. Cornelia Street
题意就是找出两个相同长度的字符串 AAA 和 BBB ,满足 AAABBBAAa能够满足题目给出的字符串,其中 aaa 是 AAA 的前缀。
- 一开始被这道题的数据范围给吓到了,但还是去试了一下看能不能过,就是枚举长度一个一个比较过去,没想到…还真的AC了,有惊有险
- 枚举长度 lll ,后面在比较串的时候一个一个比较过去就可以了,基本的操作。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 8e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
char s[N];
int pos, len, now;
bool check1(int l) {
now = (len / 2) / l * l;
bool flag = false;
for (int i = l; i < len; ++i) {
if (s[i % l] == s[i]) {
flag = true;
}
else {
now = i / l * l;
return true;
}
}
return true;
}
bool check2(int l) {
int nxt = len;
bool flag = false;
for (int i = now+l; i < len; ++i) {
if (s[i % l + now] == s[i]) {
flag = true;
}
else {
int pos = i / l * l;
for (int j = pos; j < len; ++j) {
if (s[j % l] != s[j]) {
return false;
}
}
return true;
}
}
return true;
}
int main() {
#ifdef _DEBUG
// FILE* _INPUT = freopen("input.txt", "r", stdin);
// FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
// int T;
// rd(T);
// while (T--) {
scanf(" %s", s);
len = strlen(s);
for (int i = 1; i <= len / 2; ++i) {
if (check1(i) && check2(i)) {
for (int j = 0; j < i; ++j) printf("%c", s[j]); printf(" ");
for (int j = 0; j < i; ++j) printf("%c", s[j + now]);
puts("");
break;
}
}
// }
return 0;
}
F. False God
- 根据题目意思,每个步兵都会向下移动一格,但值得注意的是,这些步兵的相对位置并不会发生改变。
- 利用相对位置不变的特性,可以把一个步兵和另外的步兵建立一个单向边,表示若金在步兵 uuu 的位置时,金可以到步兵 vvv 的位置并把步兵 vvv 吃掉
- 而金在一个位置能够吃到步兵 vvv 的条件是 abs(xu−xv)−(yv−yu)≤1abs(x_u-x_v)-(y_v-y_u)≤1abs(xu−xv)−(yv−yu)≤1, 这个可以根据金的移动规律推得。
- 在知道以上情况后,直接暴力 O(n2)O(n^2)O(n2) 对所有点进行建立单向边即可。随后跑一遍 dfsdfsdfs 得出最大深度即为答案。
上面的思路被 hack 了,暂未更新。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 1e3 + 10;
const int M = 1e7 + 10;
const ll MAX = 1e12;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
int head[N], cntE;
struct edge {
int next, to;
}e[M];
void add(int u, int v) {
e[cntE].to = v;
e[cntE].next = head[u];
head[u] = cntE++;
}
int n;
ll x[N], y[N];
ll a[N];
bool vis[N];
ll dfs(int x) {
vis[x] = true;
for (int i = head[x]; ~i; i = e[i].next) {
int v = e[i].to;
if (vis[v]) {
a[x] = max(a[x], a[v] + 1);
continue;
}
a[x] = max(a[x], dfs(v) + 1);
}
return a[x];
}
int main() {
#ifdef _DEBUG
// FILE* _INPUT = freopen("input.txt", "r", stdin);
// FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
int T; rd(T);
while (T--) {
rd(x[0]); rd(y[0]);
rd(n);
for (int i = 1; i <= n; ++i) {
rd(x[i]); rd(y[i]);
}
memset(head, -1, sizeof(int)*(n+10)); cntE = 0;
for (int i = 1; i <= n; ++i) {
if (abs(x[0] - x[i]) - (y[i] - y[0]) <= 1) {
add(0, i);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (i == j) continue;
if (abs(x[i] - x[j]) - (y[j] - y[i]) <= 1) {
add(i, j);
}
}
}
for (int i = 1; i <= n; ++i) a[i] = 1;
memset(vis, false, sizeof(bool) * (n + 10));
a[0] = 0;
printf("%lld\n", dfs(0)-1);
}
return 0;
}
G. Goodbye
老实说,这题…emmm…题目三个人都没读懂什么意思,后来我强行去看答案找规律发现
*
- 4=22,8=23,666=2∗32∗374=2^2,8=2^3,666=2*3^2*374=22,8=23,666=2∗32∗37
- 由上述规律看出来,每次有值的答案都选了最大的两个因子;并且由博弈来看,挑出两个因子的数只能进行两次操作,貌似这个猜测是正确的,然后瞎搞了一番,AC…
瞎搞才是硬道理
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
template<typename T>
inline void rd(T& x)
{
int tmp = 1; char c = getchar(); x = 0;
while (c > '9' || c < '0') { if (c == '-')tmp = -1; c = getchar(); }
while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
x *= tmp;
}
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
int prime[N]; bool isprime[N] = { false };
int cnt;
void init() {
cnt = 0;
isprime[0] = isprime[1] = true;
for (int i = 2; i <= N - 5; ++i) {
if (!isprime[i]) {
prime[++cnt] = i;
}
for (int j = 1; j <= cnt && prime[j] * i <= (N - 10); ++j) {
isprime[prime[j] * i] = true;
if (i % prime[j] == 0) break;
}
}
}
int a[N];
vector<pii>vec;
ll fp(ll x, ll y) {
ll ans = 1;
while (y) {
if (y & 1) ans *= x;
x *= x;
y >>= 1;
}
return ans;
}
bool check() {
bool flag = false;
int cnt = 0;
for (auto v : vec) {
cnt += v.second;
}
if (cnt < 3) return false;
int ans = vec[vec.size() - 1].first;
if (vec[vec.size() - 1].second > 1) ans *= vec[vec.size() - 1].first;
else ans *= vec[vec.size() - 2].first;
printf("%d\n", ans);
return true;
}
int main() {
#ifdef _DEBUG
// FILE* _INPUT = freopen("input.txt", "r", stdin);
// FILE* _OUTPUT = freopen("output.txt", "w", stdout);
#endif
init();
int T;
rd(T);
while (T--) {
int n; rd(n);
vec.clear();
if (n == 1) {
puts("0");
continue;
}
if (!isprime[n]) {
puts("0");
continue;
}
for (int i = 1; i <= cnt && prime[i] * prime[i] <= n; ++i) {
if (n % prime[i] == 0) {
int tot = 0;
while (n % prime[i] == 0) ++tot, n /= prime[i];
vec.push_back({ prime[i],tot });
}
}
if (n > 1) vec.push_back({ n,1 });
if (!check()) puts("-1");
}
return 0;
}

博客分享了比赛中几道题的解题情况。如A题求图形面积,靠直觉解题;B题是线段树结合并查集降低复杂度;C题枚举长度比较字符串;F题利用步兵相对位置建边,原思路被hack;G题通过找规律解题。
646

被折叠的 条评论
为什么被折叠?



