首页 > 其他分享 >题解 BZOJ4399 魔法少女LJJ

题解 BZOJ4399 魔法少女LJJ

时间:2023-06-09 16:33:42浏览次数:40  
标签:LJJ return log int 题解 mid BZOJ4399 mul data

前言

XXX:你瞅你长得那个B样,俺老孙就(氧化钙)......
这魔法(J8)少女能不能去死啊啊啊啊啊啊啊啊啊啊......

正文

"LJJ你是来搞笑的吧"

你说得对, 但是这数据就是骗人的.
首先看题面:

image

然后看样例:

image

最后看数据范围:

image

我惊奇的发现 \(c \le 7\) ! 家人们我真难绷qaq...

问题分析

显然第 \(8\) 条和第 \(9\) 条可以忽略, 直接看前 \(7\) 条即可.

  1. 线段树动态开点. 过于简单, 不予赘述.
  2. 线段树合并. 过于简单, 不予赘述.
  3. 很暴力地想, 因为维护的是一颗权值线段树(内部的值是单调的), 所以很容易将权值比 \(x\) 小的划分到一边(比 \(x\) 大的不用管), 先把它们删去, 记录删去了多少个点, 重新更改时把这些之前删去的值加上即可. 当然也不是不可以直接更改, 稍微在插点的时候判断一下就行.
  4. 原理同 \(3\) , 不予赘述.
  5. 查第 \(k\) 小值. 过于简单, 不予赘述.
  6. 比较两个连通块(两棵线段树)的点的乘积大小. 显然单纯求乘积一定会爆, 因为 \(\log_x(a \times b) = \log_x(a) + \log_x(b)\) , 所以我们可以通过求每一项的 \(\log_x\) 值的和来代替求每一项的积. 根据函数图像 \(f(x) = \log_a(x)\) 可知这样可以优化不少值域. 库函数提供了 \(\log 2\), \(\ln\) (即 \(\log\) ) 和 \(\log 10\). 这里不建议用 \(\log 10\), 因为它求出来的值太小, 会出现精度问题.
  7. 查连通块内点的个数. 过于简单, 不予赘述.

代码实现

想出这些远远不够, 还要在调代码上下一番功夫. 要注意的点都在下方的代码里:

#include <bits/stdc++.h>
using namespace std;

const int maxn(400005); 

inline int read() {
    int f(1), x(0);
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c & 15);
    return f * x;
}

int m, n, op[maxn], c1[maxn], c2[maxn], val[maxn];

// 离散化
int lb(int x) { return lower_bound(val + 1, val + 1 + n, x) - val; }

// 并查集优化
int fa[maxn], tot;
int getfa(int x) { return fa[x] == x ? x : fa[x] = getfa(fa[x]); }

int cnt, rt[maxn];
struct SegmentTree {
    int l, r, data;
    int lazy;
    long double mul;
};
array<SegmentTree, maxn << 5> t;

void pushup(int p) {
    t[p].data = t[t[p].l].data + t[t[p].r].data;
    t[p].mul = t[t[p].l].mul + t[t[p].r].mul;
}

void pushdown(int p) {
    if (t[p].lazy) {
        t[t[p].l].lazy = t[t[p].r].lazy = 1;
        t[t[p].l].data = t[t[p].l].mul = 0;
        t[t[p].r].data = t[t[p].r].mul = 0;
        t[p].lazy = 0;
    }
}

void insert(int& p, int l, int r, int c, double x, int y) {
    if (!p) p = ++cnt;
    if (l == r) {
        t[p].data += c;
        t[p].mul += x; // 就是这里不用再乘 c
        return;
    }

    pushdown(p);
    int mid = (l + r) >> 1;
    if (y <= mid) insert(t[p].l, l, mid, c, x, y);
    else insert(t[p].r, mid + 1, r, c, x, y);
    pushup(p);
}

void merge(int& a, int b, int l, int r) {
    if (!a || !b) {
        a = a ? a : b;
        return;
    }
    if (l == r) {
        t[a].data += t[b].data;
        t[a].mul += t[b].mul;
        return;
    }

    pushdown(a); pushdown(b);
    int mid = (l + r) >> 1;
    merge(t[a].l, t[b].l, l, mid);
    merge(t[a].r, t[b].r, mid + 1, r);
    pushup(a);
}

int ask_kth(int p, int l, int r, int k) {
    if (l == r) return val[l];
    pushdown(p);
    int mid = (l + r) >> 1;
    if (t[t[p].l].data >= k) return ask_kth(t[p].l, l, mid, k);
    else return ask_kth(t[p].r, mid + 1, r, k - t[t[p].l].data);
}

void del(int p, int l, int r, int x, int y) {
    if (!p || y < x) return;
    if (x <= l && r <= y) {
        t[p].data = t[p].mul = 0;
        t[p].lazy = 1;
        return;
    }

    int mid = (l + r) >> 1;
    pushdown(p);
    if (x <= mid) del(t[p].l, l, mid, x, y);
    if (y > mid) del(t[p].r, mid + 1, r, x, y);
    pushup(p);
}

int ask_num(int p, int l, int r, int x, int y) {
    if (!p || y < x) return 0;
    if (x <= l && r <= y) return t[p].data;
    pushdown(p);
    int mid = (l + r) >> 1, tmp(0);
    if (x <= mid) tmp += ask_num(t[p].l, l, mid, x, y);
    if (y > mid) tmp += ask_num(t[p].r, mid + 1, r, x, y);
    return tmp;
}

int main() {
    m = read();

    for (int i = 1; i <= m; ++i) {
        op[i] = read(); c1[i] = read();

        if (op[i] != 1 && op[i] != 7) c2[i] = read();
        if (op[i] == 1) val[++n] = c1[i];
        if (op[i] == 3 || op[i] == 4) val[++n] = c2[i];
    }
    // c1[] 和 c2[] 都是离散化前的权值

    // 离散化
    sort(val + 1, val + 1 + n);
    n = unique(val + 1, val + 1 + n) - (val + 1);
    // 离散化后的值域是 [1, n]
    // 最上面的 lb() 求出来的是离散化后的权值
    // log() 里面一定是离散化之前的权值, 否则求出来的成绩大小关系会改变

    for (int i = 1; i <= m; ++i) {
        if (op[i] == 1) {
            // 此时只有的 c1[] 是离散化前的点权值
            insert(rt[++tot], 1, n, 1, log(c1[i]), lb(c1[i]));
            fa[tot] = tot;
        }

        if (op[i] == 2) {
            int a = getfa(c1[i]), b = getfa(c2[i]);
            if (a != b) {
                fa[b] = a;
                merge(rt[a], rt[b], 1, n);
                // 并查集合并与线段树合并的方向要一致
            }
        }

        if (op[i] == 3) {
            int a = getfa(c1[i]), b = lb(c2[i]); // b 是离散化后的权值
            if (b == 1) continue;
            int num = ask_num(rt[a], 1, n, 1, b - 1); // num 表示要删去的有多少个点
            del(rt[a], 1, n, 1, b - 1);
            insert(rt[a], 1, n, num, num * log(c2[i]), b);
            // b 在函数对应的 y 的位置, 因为 y 要和 mid 比较, mid 又是通过离散化后的值域求出来的, 所以 y 的位置传的是离散化以后的权值 b
            // 这里面的 log() 已经乘上了 num , 因为删了 num 个点, 所以要加上 num 个 log() , 所以函数里面就不用乘 c
        }

        if (op[i] == 4) {
            int a = getfa(c1[i]), b = lb(c2[i]);
            if (b == n) continue;
            int num = ask_num(rt[a], 1, n, b + 1, n);
            del(rt[a], 1, n, b + 1, n);
            insert(rt[a], 1, n, num, num * log(c2[i]), b);
        }

        if (op[i] == 5) {
            int a = getfa(c1[i]);
            printf("%d\n", ask_kth(rt[a], 1, n, min(c2[i], t[rt[a]].data)));
        }

        if (op[i] == 6) {
            int a = getfa(c1[i]), b = getfa(c2[i]);
            printf("%d\n", t[rt[a]].mul > t[rt[b]].mul);
        }

        if (op[i] == 7) {
            int a = getfa(c1[i]);
            printf("%d\n", t[rt[a]].data); // 直接输出就行
        }
    }
}

结语

敲这B代码跟深井冰似的... 不过以后应该再也不会害怕调恶心的代码了吧...

标签:LJJ,return,log,int,题解,mid,BZOJ4399,mul,data
From: https://www.cnblogs.com/Chronologika/p/17469573.html

相关文章

  • [AGC055B] ABC Supremacy 题解
    [AGC055B]ABCSupremacy题解题目描述给定两个长度为 \(n\) 的字符串 \(a\),\(b\)。你可以进行若干次以下操作:若 \(a\) 中的一个子串为 ABC,BCA 或 CAB,那么可以将这个子串替换为 ABC,BCA 或 CAB。求能否将 \(a\) 变成 \(b\),输出 YES 或 NO。解析不难发现,......
  • chrome 跨域问题解决
    1.后端设置了跨域,https下可以,http不可以高版本chrome配置了策略,如果访问私有网络,会出现禁止跨域chrome://flags/#block-insecure-private-network-requestsBlockinsecureprivatenetworkrequests.......
  • JAVA面试题解惑系列(六)——字符串(String)杂谈
    关键字:java面试题字符串string作者:臧圩人(zangweiren)网址:http://zangweiren.javaeye.com上一次我们已经一起回顾了面试题中常考的到底创建了几个String对象的相关知识,这一次我们以几个常见面试题为引子,来回顾一下String对象相关的其它一些方面。String的l......
  • quickfix协议当有中文时校验位错误问题解决
    quickfix校验位计算都是根据ISO-8859-1编码计算,知道这个规则后续我们处理中文就很好处理了。但是如果用ISO-8859-1编码有中文会出现乱码,如果将CharsetSupport.setCharset设置为UTF-8或者GBK时,在发送数据时会报java.nio.bufferoverflowexception:null,或者校验位失败。1、往step网......
  • JSOI2018 部分题解
    目录潜入行动防御网络列队潜入行动一眼直接DP。设\(f_{i,j,0/1,0/1}\)表示\(i\)子树内放了\(j\)个监听设备,\(i\)是否被子结点覆盖,\(i\)是否放了监听设备,\(i\)子树内除了\(i\)都被覆盖的方案数。转移是一个树形背包,时间复杂度\(\mathcal{O}(nk)\),只是常数有点大。......
  • Competitive Programmer 题解
    题目传送门一道模拟题。纯模拟肯定不行,考虑优化。\(60=2^2\times3\times5\),也就是说我们判断组合后的数字能否被\(2\),\(3\),\(10\)整除即可。如果这个数能被\(2\)整除,那么原数一定会存在偶数;如果这个数能被\(3\)整除,那么它的数字和应该也能被\(3\)整除;如果这个数......
  • CF547E Mike and Friends题解
    题目链接温馨提示:做本题之前可以先尝试这个:洛谷P2414阿狸的打字机(是简单版的uwu)。首先,这个题涉及多模式串匹配,首先想AC自动机。但是有个问题:我们如何去计算一个串出现的次数呢?我们先考虑查询一个串\(a\)在串\(b\)中出现的次数。首先,在AC自动机上有一个性质,就是如果......
  • 魔法少女LJJ
    魔法少女LJJ题目描述:在森林中见过会动的树,在沙漠中见过会动的仙人掌过后,魔法少女LJJ已经觉得自己见过世界上的所有稀奇古怪的事情了LJJ感叹道“这里真是个迷人的绿色世界,空气清新、淡雅,到处散发着醉人的奶浆味;小猴在枝头悠来荡去,好不自在;各式各样的鲜花争相开放,各种树枝的......
  • rsa加解密的内容超长的问题解决
    一.现象:     有一段老代码用来加密的,但是在使用keyA的时候,抛出了异常:javax.crypto.IllegalBlockSizeException:Datamustnotbelongerthan117bytes。老代码已经做了分段的加密,应该是已经考虑了加密长度的问题才对。换了另一个线上代码中的keyB,正常加......
  • 【前端跨域】CORS跨域问题解决思路
    目录一、Nginx跨域配置二、Spring项目跨域配置参考资料一、Nginx跨域配置在Nginx中配置跨域请求,主要可以通过设置HTTP响应头部的方式进行。以下是具体实现步骤:在Nginx的配置文件中找到对应location配置块,例如:server{listen80;server_nameexample.com;......