首页 > 其他分享 >洛谷P6138 [IOI2012] 骑马比武竞赛

洛谷P6138 [IOI2012] 骑马比武竞赛

时间:2024-02-17 20:12:56浏览次数:25  
标签:sz 洛谷 int IOI2012 ++ P6138 ans 区间 return

洛谷传送门

分析

首先可以将骑士比赛这件事视为将一段区间缩为一个点。那么最后剩下的一段区间中每个点一定是有一个区间浓缩而来。展开这个区间,展开后的区间中的每个点也是由一个区间浓缩而来。这样逐步展开,会得到原本的有 \(n\) 个元素的数组。注意,虽然迟到的还没有加入,但是各区间的包含情况是不变的。所以我们将区间的包含情况建成一棵树,且称为区间树。

接下来我们考虑一个骑士什么时候会输。显然,将这个骑士代表的叶子不断往上跳,第一次跳到一个含有比他大的能力值的区间时,他就输了。为了求出迟到的放在每个位置能赢几场,我们可以对区间树进行 dfs,同时维护一个栈,栈中存当前点的所有祖先。走到叶子,就代表我们要把迟到的放在这个叶子对应的位置。那接下来就要求他往上跳多少会挂掉。

由于我们已经维护了一个存当前点所有祖先的栈,我们就可以在栈里面二分,二分出他输掉的位置。为此,我们还需要对输入的 \(n - 1\) 个元素的数组建 ST 表,以支持查区间最值。注意,由于原本的数组中并没有迟到的,所以此时查的区间会发生变化,具体细节可以看代码。

最后还剩下建区间树。发现题目实际上要支持一个区间删除,单点查值。使用平衡树维护即可。连边可以暴力。

总复杂度一个 \(\log\)。

代码

#include <iostream>
#include <random>
#define lowbit(x) ((x) & -(x))
using namespace std;
mt19937 mtrand(3225 * 2532 + 21060621);
int n, c, X;
int L[300005], R[300005];
int head[300005], nxt[600005], to[600005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int rt;
struct node {
    int l, r, sz, val;
    unsigned int prio;
} T[300005];
int tn[300005], tncnt;
int ncnt;
int New(int val) {
    T[++ncnt] = (node) { 0, 0, 1, val, mtrand() };
    return ncnt;
}
void pushup(int x) { T[x].sz = T[T[x].l].sz + T[T[x].r].sz + 1; }
void Split(int x, int& l, int& r, int k) {
    if (!x) 
        return l = r = 0, void();
    if (k >= T[T[x].l].sz + 1) {
        l = x;
        Split(T[x].r, T[l].r, r, k - T[T[x].l].sz - 1);
    } else {
        r = x;
        Split(T[x].l, l, T[r].l, k);
    }
    pushup(x);
}
int Merge(int x, int y) {
    if (!x || !y) 
        return x | y;
    if (T[x].prio > T[y].prio) {
        T[y].l = Merge(x, T[y].l);
        pushup(y);
        return y;
    } else {
        T[x].r = Merge(T[x].r, y);
        pushup(x);
        return x;
    }
}
int Kth(int x, int k) {
    if (k <= T[T[x].l].sz) 
        return Kth(T[x].l, k);
    else if (k > T[T[x].l].sz + 1) 
        return Kth(T[x].r, k - 1 - T[T[x].l].sz);
    return T[x].val;
}
void Adde(int x, int f) {
    add(f, tn[T[x].val]);
    if (T[x].l) 
        Adde(T[x].l, f);
    if (T[x].r) 
        Adde(T[x].r, f);
}
int mx[24][100005];
int lg2[300005];
int Query(int l, int r) {
    int k = lg2[r - l + 1];
    return max(mx[k][l], mx[k][r - (1 << k) + 1]);
}
int stk[300005], sz;
int ans = -1, pos = 2147483647;
bool chk(int x, int p) {
    x = stk[x];
    int l = L[x], r = R[x];
        --r;
    return Query(l, r) < X;
}
void dfs(int x) {
    if (!head[x]) {
        int l = 1, r = sz, mid, ans = sz + 1;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (chk(mid, x)) 
                ans = mid, r = mid - 1;
            else 
                l = mid + 1;
        }
        ans = sz - ans + 1;
        if (ans > ::ans) 
            ::ans = ans, pos = x;
        if (ans == ::ans) 
            pos = min(pos, x);
        return;
    }
    stk[++sz] = x;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        dfs(v);
    }
    --sz;
}
void dfs1(int x) {
    if (T[x].l) 
        dfs1(T[x].l);
    dfs(tn[T[x].val]);
    if (T[x].r) 
        dfs1(T[x].r);
}
int main() {
    cin >> n >> c >> X;
    for (int i = 1; i < n; i++) cin >> mx[0][i];
    lg2[0] = -1;
    for (int i = 1; i <= n; i++) rt = Merge(rt, New(tn[i] = i)), lg2[i] = lg2[i - 1] + (lowbit(i) == i), L[i] = R[i] = i;
    tncnt = n;
    while (c--) {
        int l, r;
        cin >> l >> r;
        ++l, ++r;
        int tl = Kth(rt, l), tr = Kth(rt, r);
        int a, b, c, d;
        Split(rt, a, b, l - 1);
        Split(b, b, c, r - l + 1);
        ++tncnt;
        L[tncnt] = L[tn[tl]], R[tncnt] = R[tn[tr]];
        Adde(b, tncnt);
        Split(b, b, d, 1);
        rt = Merge(a, Merge(b, c));
        tn[tl] = tncnt;
    }
    for (int i = 1; (1 << i) <= n - 1; i++) {
        for (int j = 1; j + (1 << i) - 1 <= n - 1; j++) 
            mx[i][j] = max(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
    }
    dfs1(rt);
    cout << pos - 1 << "\n";
    return 0;
}

另外好像有简单无数倍的线段树做法,但是我不会。

标签:sz,洛谷,int,IOI2012,++,P6138,ans,区间,return
From: https://www.cnblogs.com/forgotmyhandle/p/18018297

相关文章

  • 选课 洛谷P2014
    传送门\(\Large\textbf{问题描述}\)大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的学分。学生最后的学分是他选修的各门课的学分的总和。每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选......
  • 洛谷P6169 [IOI2016] Molecules
    洛谷传送门分析结论:如果存在解,则一定有一个解使得选的数是排序后的一段前缀并上一段后缀。下文所说序列均已排序。引理:对于一个可行解选的某个数,一定可以将其换成序列中的最小数或最大数而使得换完之后还是一个可行解。证明:反证法。假设都不可换。设当前选的所有数的和为\(......
  • 洛谷 P4065 题解
    模拟赛T1,纪念一下第一次场切紫。(话说场上这么多人切是不是都找到原了,就我这么傻想了半天)正难则反,很容易的将题目转化为选择若干种颜色,使这些颜色在原数组中的位置连续。设$pre_i$为颜色$i$最早出现的位置,$suf_i$为颜色$i$最晚出现的位置。假设通过选择若干颜色得到的位......
  • 洛谷 P9912 [COCI 2023/2024 #2] Zatopljenje 题解
    首先发现区间中的个数等于\(\texttt{高度大于x的位置的个数}-\texttt{连续两个位置都是大于x的位置的个数}\)。具体令\(H_i=\min(h_i,h_{i+1})(i\in[1,n-1])\),那么对于一次询问答案\(ans=\sum\limits_{i=l}^{r}[h_i>x]-\sum\limits_{i=l}^{r-1}[H_i>x]\),其......
  • 洛谷【算法1-5】 贪心
    P2240【深基12.例1】部分背包问题-洛谷|计算机科学教育新生态(luogu.com.cn)#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=110;intn,t;structnode{intm,v;};boolcmp(nodeaa,nodebb){returnaa.v*bb.m>bb.v*aa.m......
  • 「杂题乱刷」洛谷 P1831
    题目链接一道简单数位dp题。多设一个支点和力矩和然后套板子就做完了。参考代码:点击查看代码/*Tips:你数组开小了吗?你MLE了吗?你觉得是贪心,是不是该想想dp?一个小时没调出来,是不是该考虑换题?*/#include<bits/stdc++.h>usingnamespacestd;#definemapunordered_m......
  • 【题单】一个动态更新的洛谷综合题单
    洛谷试炼场的题目确实很具有代表性,但是近几年以来,又有许多经典题目出现在OI界中,这个大题单就是作为洛谷试炼场的扩展和补充。目录新版本食用指南更新日志题单Part0试机题Part1入门阶段Part2基础算法Part3搜索Part4动态规划Part4.1-4.4动态规划Part4.5-4.12动态规......
  • 「杂题乱刷」洛谷 P10155
    题目链接P10155[LSOT-2]基于二分查找与插入的快速排序解题思路算法一:容易发现,当\(a_n\)不为\(n\)时,我们无论如何都无法将\(n\)这个值插入到最后一位,否则我们可以依次将所有数字从大到小插入,这样也可以保证失去最少的贡献。视写法获得\(40\)分或\(60\)分。算法二......
  • 洛谷P5725 【深基4.习8】求三角形
    洛谷P5725【深基4.习8】求三角形【深基4.习8】求三角形题目描述模仿例题,打印出不同方向的正方形,然后打印三角形矩阵。中间有个空行。输入格式输入矩阵的规模,不超过9。输出格式输出矩形和正方形样例#1样例输入#14样例输出#101020304050607080910111213141516......
  • 洛谷 P1795 无穷的序列 题解
    题目传送门题目大意:给定整数\(a\),判断\(a\)是否属于数列\(1,2,4,7,11\cdots\)。多测。1.暴力枚举(90pts)不难发现,该数列除第一项外第\(n\)项比第\(n-1\)项多\(n-1\)。故暴力枚举\(n\),计算数列的每一项,判断是否与\(a\)相等,大于\(a\)就break。多测加记忆化,用mx......