Gym - 101630 部分题解
前言
有几题没补…但感觉这些题目都是不错的…所以…会抽时间补一下的吧???
不过大概率是咕咕咕的…
正文
Gym-101630A. Archery Tournament
- 题意
- 给你一些圆(都与 x x x轴相切)还有一些询问(边询问边插入圆) 问这些点是否在一个圆上…如果是则把这个圆给删除…(保证同一时刻圆不会相交)
- − 1 e 9 ≤ x , y ≤ 1 e 9 -1e9 \leq x,y \leq 1e9 −1e9≤x,y≤1e9, n ≤ 1 e 5 n \leq 1e5 n≤1e5
- 题解
- 线段树!(
smg) - 线段树维护一段区间内的圆的标号…感觉有点像那个K-Dtree…???确定了一个范围…
- 我们可以知道被一条 x = a x=a x=a的直线切到的圆最多在 l o g log log
- 虽然是区间修改…但是却不需要什么lazy的标记…而且也不需要区间合并操作…
- 线段树标记永久化-原理就是: 在路过该节点的时候把修改对答案的影响加上,来省去标记下放的过程
- 线段树!(
#include <bits/stdc++.h>
#define ls x << 1
#define rs x << 1 | 1
#define ll long long
using namespace std;
const int N = 4e5 + 10;
struct data {
int tp, x, y;
} q[N];
struct node {
set<int> id;
} tr[N << 2];
int all[N * 3], tot, ans;
int get(int x) {
return lower_bound(all + 1, all + 1 + tot, x) - all;
}
void upd(int x, int l, int r, int L, int R, int w) {
if(L <= l && r <= R) {
tr[x].id.insert(w);
return ;
}
int mid = (l + r) >> 1;
if(L <= mid) upd(ls, l, mid, L, R, w);
if(R > mid) upd(rs, mid + 1, r, L, R, w);
}
void ers(int x, int l, int r, int L, int R, int w) {
if(L <= l && r <= R) {
tr[x].id.erase(w);
return ;
}
int mid = (l + r) >> 1;
if(L <= mid) ers(ls, l, mid, L, R, w);
if(R > mid) ers(rs, mid + 1, r, L, R, w);
}
ll pf(ll x) {
return x * x;
}
bool check(int i, int j) {
return pf(q[i].x - q[j].x) + pf(q[i].y - q[j].y) < pf(q[i].y);
}
void get(int x, int l, int r, int L, int w) {
set<int> :: iterator sit;
for(sit = tr[x].id.begin(); sit != tr[x].id.end(); ++sit) {
int pos = *sit;
if(check(pos, w)) {
// printf("q%d %d\n", q[pos].x, q[pos].y);
// printf("p%d %d\n", q[w].x, q[w].y);
ans = pos;
}
}
int mid = (l + r) >> 1;
if(l == r || ans != -1) return ;
if(L <= mid) get(ls, l, mid, L, w);
else get(rs, mid + 1, r, L, w);
}
int main() {
int n, cnt = 0;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d%d%d", &q[i].tp, &q[i].x, &q[i].y);
if(q[i].tp == 1) {
all[++cnt] = q[i].x - q[i].y, all[++cnt] = q[i].x + q[i].y;
} else {
all[++cnt] = q[i].x;
}
}
sort(all + 1, all + 1 + cnt);
tot = unique(all + 1, all + 1 + cnt) - all - 1;
for(int i = 1; i <= n; ++i) {
if(q[i].tp == 1) {
int l = get(q[i].x - q[i].y);
int r = get(q[i].x + q[i].y);
upd(1, 1, tot, l, r, i);
} else {
int x = get(q[i].x);
ans = -1;
get(1, 1, tot, x, i);
printf("%d\n", ans);
if(ans != -1) {
int l = get(q[ans].x - q[ans].y);
int r = get(q[ans].x + q[ans].y);
ers(1, 1, tot, l, r, ans);
}
}
}
return 0;
}
Gym-101630I. Journey from Petersburg to Moscow
-
题意
- 求 1 − n 1-n 1−n的最短路…最短路定义为前 k k k大边权的和
- n ≤ 3000 n \leq 3000 n≤3000
-
题解
神仙题!…看上去现场也没有什么人过的样子…其实这道题也挺可做的呀…qwq
首先我们需要知道一点…这样定义的最短路一定会比我们原来的最短路要小
-
如果我们找到了一条路径…它的边数 ≤ k \leq k ≤k 那么这个路径的价值就是所有权值和也就是最短路
-
如果$k \leq 边 数 , 对 于 这 种 情 况 . 我 们 就 枚 举 这 个 第 k 大 的 边 权 边数,对于这种情况.我们就枚举这个第k大的边权 边数,对于这种情况.我们就枚举这个第k大的边权c . . 所 有 小 于 ..所有小于 ..所有小于c$的边权变为 0 0 0,大于 c c c的边权变为 w − c w-c w−c
跑一遍最短路…用 d i s [ n ] + c ∗ k dis[n]+c*k dis[n]+c∗k更新答案
-
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3010;
struct data {
int nt, to, w;
} a[N << 1];
int cnt = 0;
int head[N];
ll dis[N], v[N];
typedef pair<ll, int> P;
priority_queue<P, vector<P>, greater<P> > q;
void add(int x, int y, int w) {
a[++cnt].to = y;
a[cnt].w = w;
a[cnt].nt = head[x];
head[x] = cnt;
}
int n, m, k;
void go(ll w) {
for(int i = 1; i <= n; ++i) {
dis[i] = 1e13;
}
dis[1] = 0;
q.push(P(0, 1));
while(!q.empty()) {
P p = q.top(); q.pop();
int v = p.second;
if(dis[v] < p.first) continue;
for(int i = head[v]; i; i = a[i].nt) {
int to = a[i].to;
ll now = max(a[i].w - w, 0ll);
if(dis[to] > dis[v] + now) {
dis[to] = dis[v] + now;
q.push(P(dis[to], to));
}
}
}
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for(int i = 1; i <= m; ++i) {
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
add(x, y, w);
add(y, x, w);
v[i] = w;
}
ll ans = 1e18;
v[++m] = 0;
for(int i = 1; i <= m; ++i) {
go(v[i]);
// printf("%lld %lld\n", v[i], dis[n]);
ans = min(ans, dis[n] + v[i] * k);
}
printf("%lld\n", ans);
return 0;
}
Gym101630C. Connections
- 题意
- 给你一张 n n n个点 m m m条边的有向图,保证两两之间可以到达…请你删除 m − 2 ∗ n m-2*n m−2∗n条边…使之还可以两两之间到达
- n , m ≤ 100000 n,m \leq 100000 n,m≤100000
- 题解
- 首先我们肯定是可以构造出来这样的图的!
- 每加入一个环最坏的情况是只加入一个点…那么最多需要 2 ∗ n − 1 2*n-1 2∗n−1条边
- 我们只需要保证所有的点都能从1走到且都能走到1
- 所以正向反向搜一遍dfs树…所有没有被用作树边的边都是可以删除的
- 首先我们肯定是可以构造出来这样的图的!
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
struct data {
int nt, to;
} a[N][2];
int head[N][2], cnt[2], vis[N], vs[N];
void add(int x, int y, int id) {
a[++cnt[id]][id].to = y;
a[cnt[id]][id].nt = head[x][id];
head[x][id] = cnt[id];
}
void dfs(int x, int id) {
vs[x] = 1;
for(int i = head[x][id]; i; i = a[i][id].nt) {
int to = a[i][id].to;
if(!vs[to]) {
vis[i]++;
dfs(to, id);
}
}
}
int main() {
int T;
scanf("%d", &T);
for(int o = 1; o <= T; ++o) {
int n, m;
cnt[0] = cnt[1] = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
head[i][0] = head[i][1] = 0;
for(int i = 1, x, y; i <= m; ++i) {
scanf("%d%d", &x, &y);
vis[i] = 0;
add(x, y, 0);
add(y, x, 1);
}
for(int i = 1; i <= n; ++i)
vs[i] = 0;
dfs(1, 0);
for(int i = 1; i <= n; ++i)
vs[i] = 0;
dfs(1, 1);
int now = m - 2 * n;
// for(int i = 1; i <= m; ++i) {
// printf("%d %d\n", i, vis[i]);
// }
for(int u = 1; u <= n; ++u) {
for(int i = head[u][0]; i; i = a[i][0].nt) {
if(!vis[i] && now) {
printf("%d %d\n", u, a[i][0].to);
now--;
}
}
}
}
return 0;
}
Gym101630L - Laminar Family
-
题意
- 定义 f f f集合合法为 f f f中任意两个集合 A , B A,B A,B A 属 于 B ∣ ∣ B 属 于 A ∣ ∣ A 与 B 无 交 A属于B||B属于A||A与B无交 A属于B∣∣B属于A∣∣A与B无交
- 给你一个 n n n个节点的树 给你 m m m组路径 ( u , v ) (u,v) (u,v) 问这 m m m组路径是否满足条件
-
题解
-
比赛时候 A − E A-E A−E都没打出来根本没心情看这种题目啊!…后面真的有几题是可做的呢
-
首先 u = = v u==v u==v的组合是没有用的
-
我们可以树上差分记录每个节点被访问到了几次
-
去除没有访问过的点
-
很显然…如果有度数>2的点肯定是不行的!
-
接下来我们要做的就是一个经典问题了对吧…
有一些线段是否存在交叉关系
- 这个搞个单调栈什么的就可以了…具体看一下代码吧
-
-
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, LOG = 20;
struct Path {
int len, u, v;
} b[N];
struct data {
int nt, to;
} a[N << 1];
queue<int> q;
vector<int> G[N];
int cnt = 0, n, m;
int val[N], ys[N], sy[N], vis[N], st[N];
int d[N], head[N], dep[N], g[N][LOG + 1];
void add(int x, int y) {
a[++cnt].to = y;
a[cnt].nt = head[x];
head[x] = cnt;
}
void dfs(int u, int fa) {
g[u][0] = fa;
dep[u] = dep[fa] + 1;
for(int i = head[u]; i; i = a[i].nt) {
int to = a[i].to;
if(to == fa) {
continue;
}
dfs(to, u);
}
}
void Dfs(int u, int fa) {
for(int i = head[u]; i; i = a[i].nt) {
int to = a[i].to;
if(to == fa) {
continue;
}
Dfs(to, u);
val[u] += val[to];
}
}
void yyqx() {
for(int j = 1; j <= LOG; ++j)
for(int i = 1; i <= n; ++i)
g[i][j] = g[g[i][j - 1]][j - 1];
}
int GetLCA(int A, int B) {
if(dep[A] > dep[B]) swap(A, B);
for(int i = LOG; i >= 0; --i)
if(dep[g[B][i]] >= dep[A]) B = g[B][i];
if(A == B) return A;
for(int i = LOG; i >= 0; --i)
if(g[A][i] != g[B][i])
A = g[A][i], B = g[B][i];
return g[A][0];
}
bool cmp(Path A, Path B) {
return A.len > B.len;
}
bool go(int be) {
vis[be] = 1;
q.push(be);
int now = 0;
while(!q.empty()) {
int h = q.front(); q.pop();
sy[h] = ++now, ys[now] = h;
for(int i = head[h]; i; i = a[i].nt) {
int to = a[i].to;
if(!vis[to] && val[to]) {
vis[to] = 1;
q.push(to);
break;
}
}
}
int top = 0;
for(int i = 1; i <= now; ++i) {
int h = ys[i];
while(top && st[top] < i) top--;
for(int j = 0; j < G[h].size(); ++j) {
int to = G[h][j];
if(sy[to] <= i) continue;
if(!top || sy[to] <= st[top]) st[++top] = sy[to];
else return false;
}
}
return true;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1, x, y; i < n; ++i) {
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
dfs(1, 0), yyqx();
int tot = 0;
for(int i = 1, x, y; i <= m; ++i) {
scanf("%d%d", &x, &y);
if(x == y) continue;
int lca = GetLCA(x, y);
// printf("%d %d %d\n", x, y, lca);
b[++tot].u = x, b[tot].v = y, b[tot].len = dep[x] + dep[y] - 2 * dep[lca];
val[x]++, val[y]++;
val[lca] -= 1;
val[g[lca][0]] -= 1;
}
Dfs(1, 0);
for(int u = 1; u <= n; ++u) {
if(val[u]) {
for(int i = head[u]; i; i = a[i].nt) {
int to = a[i].to;
if(val[to]) {
d[u]++;
}
}
}
}
for(int i = 1; i <= n; ++i) {
if(d[i] > 2) return printf("No\n"), 0;
}
sort(b + 1, b + 1 + tot, cmp);
for(int i = 1; i <= tot; ++i) {
G[b[i].u].push_back(b[i].v);
G[b[i].v].push_back(b[i].u);
}
for(int i = 1; i <= n; ++i) {
if(!vis[i] && d[i] == 1) {
if(!go(i)) return printf("No\n"), 0;
}
}
printf("Yes\n");
return 0;
}
Gym101630 - I . Interactive Sort
-
题意
- 一个长度 n ≤ 10000 n \leq 10000 n≤10000的一个排列…分成偶数和奇数两个数组(保证随机)
- 每次询问可以得到 e v e n [ i ] 和 o d d [ j ] even[i]和odd[j] even[i]和odd[j]的大小关系(询问次数 ≤ 300000 \leq 300000 ≤300000
-
题解
- 我们可以利用奇数将偶数分成若干段
- 每次求奇数的值的时候直接二分出在偶数的哪一个段
- 然后直接暴力拆解就行了
- 由于是随机的所以 O ( n l o g n ) O(nlog_n) O(nlogn)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
vector<int> g[N], u, v;
char lx[5];
int e[N], o[N];
int tot = 0;
bool Ask(int x, int y) {
printf("? %d %d\n", x, y);
fflush(stdout);
scanf("%s", lx + 1);
return lx[1] == '<';
}
void split(int now, int x) {
u.clear(), v.clear();
for(int i = 0; i < g[now].size(); ++i) {
if(Ask(g[now][i], x))
u.push_back(g[now][i]);
else
v.push_back(g[now][i]);
}
}
int insert(int x) {
for(int i = tot; i > x; --i)
g[i + 1] = g[i];
g[x] = u, g[x + 1] = v, tot++;
int ans = 0;
for(int i = 1; i <= x; ++i)
ans += (int)g[i].size();
return ans * 2 + 1;
}
int main() {
int n;
scanf("%d", &n);
if(n == 1) {
printf("! 1\n");
fflush(stdout);
return 0;
}
tot = 1;
for(int i = 1; i <= n / 2; ++i) {
g[1].push_back(i);
}
// printf("%d\n", (int)g[1].size());
for(int i = 1; i <= (n + 1) / 2; ++i) {
int l = 1, r = tot, ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(Ask(g[mid][0], i)) {
ans = mid, l = mid + 1;
} else {
r = mid - 1;
}
}
// printf("%d\n", ans);
if(!ans) {
// printf("???\n");
split(1, i);
if(!u.size()) {
o[i] = 1;
} else if(!v.size()) {
o[i] = u.size() * 2 + 1;
} else {
o[i] = insert(1);
}
// printf(">??\n");
} else {
int now = 0;
for(int i = 1; i < ans; ++i)
now += (int)g[i].size();
split(ans, i);
if(!v.size()) {
if(ans == tot) {
o[i] = n;
} else {
now += (int)g[ans].size();
split(ans + 1, i);
if(!u.size()) {
o[i] = now * 2 + 1;
} else {
o[i] = insert(ans + 1);
}
}
} else {
o[i] = insert(ans);
}
}
}
int cur = 0;
for(int i = 1; i <= tot; ++i)
for(int j = 0; j < g[i].size(); ++j)
cur++, e[g[i][j]] = cur * 2;
printf("! ");
for(int i = 1; i <= n / 2; ++i) {
printf("%d", e[i]);
if(i != n / 2) printf(" ");
}
for(int i = 1; i <= (n + 1) / 2; ++i)
printf(" %d", o[i]);
puts("");
fflush(stdout);
return 0;
}