赛时发现 G 和 Ex 是原题而且自己都做过,直接贺了过来/kx
F 因为是算几只知道做法不是很会写。
rk23 赢麻了/kx,大概是最高名次了/kx
A - Adjacent Squares
简单特判。
signed main() {
int H = read(), W = read();
int R = read(), C = read();
int ans = 0;
ans += (R > 1);
ans += (C > 1);
ans += (R < H);
ans += (C < W);
cout << ans << "\n";
return 0;
}
B - Enlarged Checker Board
简单模拟。
int f[1010][1010];
signed main() {
int n = read(), a = read(), b = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
for(int l = 1; l <= a; ++l)
for(int r = 1; r <= b; ++r)
if((i + j) & 1) f[(i - 1) * a + l][(j - 1) * b + r] = 1;
for(int i = 1; i <= n * a; ++i) {
for(int j = 1; j <= n * b; ++j)
cout << (f[i][j] == 1 ? '#' : '.');
puts("");
}
return 0;
}
C - Adjacent Swaps
数组的简单应用。
signed main() {
int n = read(), Q = read();
vector<int> a(n + 2), pre(n + 2);
for(int i = 1; i <= n; ++i) a[i] = i, pre[i] = i;
for(int i = 1; i <= Q; ++i) {
int x = read(), u, v;
if(pre[x] == n) {
u = pre[x], v = pre[x] - 1;
} else {
u = pre[x], v = pre[x] + 1;
}
swap(a[u], a[v]);
pre[a[u]] = u, pre[a[v]] = v;
}
for(int i = 1; i <= n; ++i) cout << a[i] << " "; puts("");
return 0;
}
D - 250-like Number
一开始被题意坑了,以为 \(q\) 可以不是素数。
考虑暴力枚举素数。
因为 \(n \le 10^{18}\) 且 \(p < q\),所以 \(p < 10^5\)。
对于 \(q\) 的话只需要枚举 \(10^6\) 内的素数即可。
然后线筛一下 \(10^6\) 以内的所有素数暴力枚举就好了。
反正跑起来挺快的,复杂度大概是 \(10^5\) 带个 \(\log\) 这种级别。
int n, ans = 0;
int prim[MAXN], Cnt = 0;
bool vis[MAXN];
void Init(int M) {
for(int i = 2; i <= M; ++i) {
if(!vis[i]) prim[++Cnt] = i;
for(int j = 1; j <= Cnt && i * prim[j] <= M; ++j) {
vis[i * prim[j]] = true;
if(i % prim[j] == 0) break;
}
}
}
void Print(__int128 x){
if(x > 9) Print(x / 10);
putchar(x % 10 + '0');
}
signed main() {
Init(1000000); n = read();
for(int i = 1; i <= Cnt; ++i) {
int p = prim[i];
for(int k = i + 1; k <= Cnt; ++k) {
int j = prim[k];
__int128 x = (__int128)p * j * j * j, y = n;
if(x > y) break;
ans ++;
}
}
cout << ans << "\n";
return 0;
}
E - Prefix Equality
一眼题。
考虑处理出某个数在另一个数组第一次出现的位置。
可以先离散化。
以 \(a\) 数组为例,处理出 \(a\) 数组的每一项在 \(b\) 数组中第一次出现的位置,然后建立线段树。
对于每次查询 \(x,y\),就是查询 \(a\) 数组的前 \(x\) 项中是不是所有元素都在 \(b\) 的前 \(y\) 项中出现,即判断 \(1 \sim x\) 中的最大值是不是 \(\le y\)。
然后对 \(b\) 也类似处理一下即可。
int n, Q;
int a[MAXN], b[MAXN];
int date[MAXN << 1], Cnt = 0, top = 0;
int vis[MAXN << 1], fir[MAXN << 1];
int Vis[MAXN << 1], Fir[MAXN << 1];
struct Seg {
#define lson i << 1
#define rson i << 1 | 1
int Max[MAXN << 2];
void Push_up(int i) { Max[i] = max(Max[lson], Max[rson]); }
void Build(int i, int l, int r, int k) {
if(l == r) { Max[i] = k ? Vis[l] : vis[l]; return ;}
int mid = (l + r) >> 1;
Build(lson, l, mid, k), Build(rson, mid + 1, r, k);
Push_up(i);
}
int Query(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return Max[i];
int mid = (l + r) >> 1, ans = 0;
if(mid >= L) ans = max(ans, Query(lson, l, mid, L, R));
if(mid < R) ans = max(ans, Query(rson, mid + 1, r, L, R));
return ans;
}
}seg[2];
signed main() {
n = read();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= n; ++i) b[i] = read();
for(int i = 1; i <= n; ++i) date[++top] = a[i], date[++top] = b[i];
sort(date + 1, date + top + 1);
Cnt = unique(date + 1, date + top + 1) - date - 1;
for(int i = 1; i <= n; ++i) a[i] = lower_bound(date + 1, date + Cnt + 1, a[i]) - date;
for(int i = 1; i <= n; ++i) b[i] = lower_bound(date + 1, date + Cnt + 1, b[i]) - date;
for(int i = 1; i <= Cnt; ++i) fir[i] = n + 1, Fir[i] = n + 1;
for(int i = n; i >= 1; --i) fir[b[i]] = i, Fir[a[i]] = i;
for(int i = n; i >= 1; --i) vis[i] = fir[a[i]], Vis[i] = Fir[b[i]];
// for(int i = 1; i <= n; ++i) cout << vis[i] << " "; puts("");
Q = read();
seg[0].Build(1, 1, n, 0);
seg[1].Build(1, 1, n, 1);
for(int i = 1; i <= Q; ++i) {
int x = read(), y = read();
int res1 = seg[0].Query(1, 1, n, 1, x);
int res2 = seg[1].Query(1, 1, n, 1, y);
// cout << x << " " << y << "\n";
if(res1 <= y && res2 <= x) puts("Yes");
else puts("No");
}
return 0;
}
F - One Fourth
计算几何题/px
考虑固定一个点,然后找到面积是最接近总面积 \(\frac{1}{4}\) 和 \(\frac{3}{4}\) 的地方,然后类似于双指针同时顺时针或者逆时针移动这些点即可,对所有结果取一个最优值。
G - Stonks
原题:CF865D
好像是简单反悔贪心。
就是从前向后扫,拿一个小根堆维护我们可能在哪些天买(具体天数不需要,只需要价格
如果发现当前这个值 \(p_i\) 大于堆顶 \(p_j\),那么我们可以在第 \(j\) 天买入第 \(i\) 天卖出,因此有一个 \(p_i - p_j\) 的贡献。然后我们把堆顶元素弹出,加入 \(p_i\) 即可。
如果后面有 \(p_k > p_i\),那么同样操作时会得到 \(p_k - p_i\) 的贡献,两次的贡献和相当于 \(p_k - p_j\),因此这么反悔贪心是对的。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, ans = 0;
priority_queue<int, vector<int>, greater<int> > q;
signed main()
{
cin >> n;
for(int i = 1, x; i <= n; ++i) {
cin >> x;
if(!q.empty() && q.top() < x) ans += x - q.top(), q.pop(), q.push(x);
q.push(x);
}
printf("%lld\n", ans);
return 0;
}
Ex - Trespassing Takahashi
原题:CF1253F
和原题唯一的区别就是最后的判断。
可以去看我在洛谷写的题解:link。
这里还有一个在博客园的链接:link
/*
思路极其鬼畜:
1、最短路
2、生成树
3、倍增+LCA
4、查询
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
//#include<time.h>
#define LL long long
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 4e5+5;
const int MAXM = 6e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
struct edge{
int from, to, w, nxt;
}e[MAXM << 2], E[MAXM], E2[MAXM << 1];
int head[MAXN], num_edge = 0;
int Head[MAXN], Num_edge = 0;
struct node{
int bh, val;
bool operator < (const node &b) const { return val > b.val; }
};
int n, m, k, Q, cnt;
int dis[MAXN], fa[MAXN], f[MAXN][22], dep[MAXN], maxm[MAXN][22];
bool vis[MAXN];
priority_queue<node> q;
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
return f ? -s : s;
}
bool cmp(edge x, edge y){ return x.w < y.w; }
void add_edge(int from, int to, int w){ e[++num_edge] = (edge){from, to, w, head[from]}, head[from] = num_edge; }
void Add_edge(int from, int to, int w){ E2[++Num_edge] = (edge){from, to, w, Head[from]}, Head[from] = Num_edge; }
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void dij(){
memset(dis, 0x3f, sizeof dis);
dis[0] = 0;
q.push((node){0, 0});
while(!q.empty()){
node u = q.top(); q.pop();
if(vis[u.bh]) continue;
vis[u.bh] = true;
for(int i = head[u.bh]; i; i = e[i].nxt){
int v = e[i].to;
if(dis[v] > dis[u.bh] + e[i].w){
dis[v] = dis[u.bh] + e[i].w;
if(!vis[v]) q.push((node){v, dis[v]});
}
}
}
}
void kruskal(){
for(int i = 1; i <= n; ++i) fa[i] = i;//重置父亲
for(int i = 1; i <= m; ++i){
int uf = find(E[i].from), vf = find(E[i].to);
if(uf != vf){
fa[vf] = uf;
Add_edge(E[i].from, E[i].to, E[i].w), Add_edge(E[i].to, E[i].from, E[i].w);//建出最小生成树来
cnt++;
if(cnt == n - 1) return ;//如果建了n - 1条边,就结束
}
}
}
void dfs(int u, int fa){//dfs预处理lca
f[u][0] = fa;
for(int i = Head[u]; i; i = E2[i].nxt){
int v = E2[i].to;
if(v == fa) continue;
dep[v] = dep[u] + 1;
maxm[v][0] = E2[i].w;
dfs(v, u);
}
}
void init(){//预处理lca
for(int i = 1; i <= 20; ++i)
for(int j = 1; j <= n; ++j)
f[j][i] = f[f[j][i - 1]][i - 1],
maxm[j][i] = max(maxm[j][i - 1], maxm[f[j][i - 1]][i - 1]);
}
int get_max(int x, int y){
int ans = 0;
if(dep[x] < dep[y]) swap(x, y);
for(int i = 20; i >= 0; --i){
if(dep[f[x][i]] < dep[y]) continue;
ans = max(ans, maxm[x][i]);
x = f[x][i];
}
if(x == y) return ans;
for(int i = 20; i >= 0; --i){
if(f[x][i] == f[y][i]) continue;
ans = max(ans, max(maxm[x][i], maxm[y][i]));
x = f[x][i], y = f[y][i];
}
ans = max(ans, max(maxm[x][0], maxm[y][0]));
return ans;
}
signed main() {
n = read(), m = read(), k = read();
for(int i = 1, u, v, w; i <= m; ++i){
u = read(), v = read(), w = read();
add_edge(u, v, w), add_edge(v, u, w);
E[i].from = u, E[i].to = v, E[i].w = w;
}
for(int i = 1; i <= k; ++i) add_edge(0, i, 0), add_edge(i, 0, 0);
dij();
for(int i = 1; i <= m; ++i) E[i].w += dis[E[i].from] + dis[E[i].to];
sort(E + 1, E + m + 1, cmp);
kruskal(); dep[1] = 1; dfs(1, -1); init(); Q = read();
for(int i = 1, u, v, t; i <= Q; ++i){
u = read(), v = read(), t = read();
int res = get_max(u, v);
if(res <= t) puts("Yes");
else puts("No");
}
return 0;
}