[lnsyoj2115] Darko 的游戏
题意
sol
假设图已经被建好了,那么问题就被转化为,在一个图上给定起点求走 步的权值为多少,倍增解决即可,接下来考虑如何建图。
分析需求,可以发现只需要求出一个数在区间内的非严格前驱和后继,再进行比较即可。
前驱和后继可以使用线段树或平衡树解决,由于可持久化平衡树很少用,因此考虑可持久化线段树解决。
对于整个序列,只需要支持查询一个数的排名和排名对应的数即可求得前驱和后继,子区间相同,只需要将权值变为两个版本的差即可。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI;
const int N = 500005, K = 18;
PLI ne[N][K + 5];
int n, m;
int a[N], b[N], ha[N], rk[N];
int rt[N], idx;
int l[N], r[N];
struct Node {
int val;
int l, r;
} tr[N << 5];
int newnode(){
idx ++ ;
tr[idx].l = tr[idx].r = tr[idx].val = 0;
return idx;
}
void build(int &u, int l, int r){
if (!u) u = newnode();
if (l == r) return ;
int mid = l + r >> 1;
build(tr[u].l, l, mid), build(tr[u].r, mid + 1, r);
}
void insert(int &u, int od, int l, int r, int x){
if (!u) u = newnode();
tr[u] = tr[od];
tr[u].val ++ ;
if (l == r) return ;
int mid = l + r >> 1;
if (x <= mid) insert(tr[u].l = 0, tr[od].l, l, mid, x);
else insert(tr[u].r = 0, tr[od].r, mid + 1, r, x);
}
int get_val(int u, int od, int l, int r, int k){
if (l == r) return l;
int s = tr[tr[u].l].val - tr[tr[od].l].val;
// printf("?!%d %d %d %d\n", l, r, s, k);
int mid = l + r >> 1;
if (s >= k) return get_val(tr[u].l, tr[od].l, l, mid, k);
else return get_val(tr[u].r, tr[od].r, mid + 1, r, k - s);
}
int get_rk(int u, int od, int l, int r, int x, int s){
if (l == r) return s + 1;
int ss = tr[tr[u].l].val - tr[tr[od].l].val;
int mid = l + r >> 1;
if (x <= mid) return get_rk(tr[u].l, tr[od].l, l, mid, x, s);
else return get_rk(tr[u].r, tr[od].r, mid + 1, r, x, s + ss);
}
int get_prev(int l, int r, int x){
// printf("%d %d %d %d\n", l, r, x, get_rk(rt[r], rt[l - 1], 0, n + 1, x, 0));
return get_val(rt[r], rt[l - 1], 0, n + 1, get_rk(rt[r], rt[l - 1], 0, n + 1, x, 0) - 1);
}
int get_next(int l, int r, int x){
return get_val(rt[r], rt[l - 1], 0, n + 1, get_rk(rt[r], rt[l - 1], 0, n + 1, x, 0));
}
void build_graph(){
for (int i = 1; i <= n; i ++ ) ha[i] = b[i] = a[i];
sort(ha + 1, ha + n + 1);
for (int i = 1; i <= n; i ++ ) b[i] = lower_bound(ha + 1, ha + n + 1, b[i]) - ha, rk[b[i]] = i;
build(rt[0], 0, n + 1);
for (int i = 1; i <= n; i ++ ) insert(rt[i], rt[i - 1], 0, n + 1, b[i]);
for (int i = 1; i <= n; i ++ ) {
int p = get_prev(l[i], r[i], b[i]);
int nx = get_next(l[i], r[i], b[i]);
// printf("##%d %d %d %d %d\n", i, p, nx, ha[p], ha[nx]);
int ans;
if (!p || nx <= n && ha[nx] - a[i] < a[i] - ha[p]) ans = nx;
else ans = p;
int pos = rk[ans];
ne[i][0] = {abs(ha[ans] - a[i]), pos};
}
// for (int i = 1; i <= n; i ++ ) {
// printf("!%d %d %lld\n", i, ne[i][0].y, ne[i][0].x);
// }
}
void init(){
for (int i = 1; i <= K; i ++ )
for (int j = 1; j <= n; j ++ )
ne[j][i] = {ne[j][i - 1].x + ne[ne[j][i - 1].y][i - 1].x, ne[ne[j][i - 1].y][i - 1].y};
}
LL find(int u, int k){
LL res = 0;
for (int i = K; i >= 0; i -- ){
int step = 1 << i;
if (k < step) continue;
res += ne[u][i].x;
u = ne[u][i].y;
k -= step;
}
return res;
}
int main(){
freopen("data1.in", "r", stdin);
freopen("data1.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &l[i], &r[i]);
build_graph();
// for (int u = 1; u <= idx; u ++ ) printf("(%d %d %d) ", tr[u].l, tr[u].r, tr[u].val);
// for (int i = 0; i < 4; i ++ ) printf("%d ", rt[i]);
init();
scanf("%d", &m);
while (m -- ){
int u, v, k;
scanf("%d%d%d", &u, &v, &k);
LL x = find(u, k), y = find(v, k);
// printf("D%lld %lld\n", x, y);
if (x > y) puts("win");
if (x < y) puts("lose");
if (x == y) puts("draw");
}
}
分类:
题解 / 2025赛时
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现