• 洛谷 P3722 [AH2017/HNOI2017]影魔


    奈文摩尔有 (n) 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 (1)(n)。第 (i) 个灵魂的战斗力为 (k_i),灵魂们以点对的形式为影魔提供攻击力。对于灵魂对 (i, j (ilt j)) 来说,若不存在 (k_s (ilt slt j)) 大于 (k_i) 或者 (k_j),则会为影魔提供 (p_1) 的攻击力。另一种情况,令 (c)(k_{i + 1}, k_{i + 2}, cdots, k_{j -1}) 的最大值,若 (c) 满足:(k_i lt c lt k_j),或者 (k_j lt c lt k_i),则会为影魔提供 (p_2) 的攻击力,当这样的 (c) 不存在时,自然不会提供这 (p_2) 的攻击力;其他情况的点对,均不会为影魔提供攻击力。

    影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任意一段区间 ([a, b]),位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑所有满足 (ale ilt jle b) 的灵魂对 (i, j) 提供的攻击力之和。

    顺带一提,灵魂的战斗力组成一个 (1)(n) 的排列:(k_1, k_1, cdots, k_n)

    简明题意:有一个n的排列k,对于一个区间([l,r]),如果(k_l)(k_r)分别是这个区间的最大值和次大值,那么产生(p_1)的贡献,如果(s=max_{i=l+1}^{r-1} k_i)满足(k_l<s<k_r),则会产生(p_2)的贡献,每次询问一个区间的答案,答案为这个区间所有子区间产生的贡献

    乍一看不太能做的样子,所以我们考虑离线操作

    先对每个i处理出向左第一个比i大的数的下标(L_i),向右第一个比i大的数的下标(R_i),这个可以直接用单调栈求出

    然后考虑产生的贡献

    1. 区间([L_i,R_i])会产生(p_1)的贡献
    2. 所有左端点在([L_i+1,i-1]),右端点是(R_i)的区间会产生(p_2)的贡献
    3. 所有左端点是(L_i),右端点在([i+1,R_i-1])的区间会产生(p_2)的贡献

    于是我们离线之后每次扫到(R_i)时就做一次贡献,把询问拆成r询问一次答案减去l-1询问一次的答案,然后就做完了

    不要忘了所有的([i,i+1])也会产生(p_1)的贡献

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #define zrt k << 1
    #define yrt k << 1 | 1
    const int N = 2e5;
    using namespace std;
    struct Q
    {
        int x,id,typ,l,r;
    }q[N * 2 + 5];
    int n,p1,p2,m,k[N + 5],L[N + 5],R[N + 5],stk[N + 5],top,Q_cnt,m_cnt;
    long long ans[N + 5];
    int cmp(Q x,Q y)
    {
        return x.x < y.x;
    }
    struct Seg
    {
        long long sm[N * 4 + 5],tag[N * 4 + 5];
        void jia(int k,int l,int r,long long z)
        {
            tag[k] += z;
            sm[k] += z * (r - l + 1);
        }
        void pushdown(int k,int l,int r,int mid)
        {
            if (tag[k])
            {
                jia(zrt,l,mid,tag[k]);
                jia(yrt,mid + 1,r,tag[k]);
                tag[k] = 0;
            }
        }
        void pushup(int k)
        {
            sm[k] = sm[zrt] + sm[yrt];
        }
        void add(int k,int l,int r,int x,int y,int z)
        {
            if (x > y)
                return;
            if (l >= x && r <= y)
            {
                tag[k] += z;
                sm[k] += 1ll * z * (r - l + 1);
                return;
            }
            int mid = l + r >> 1;
            pushdown(k,l,r,mid);
            if (x <= mid)
                add(zrt,l,mid,x,y,z);
            if (y > mid)
                add(yrt,mid + 1,r,x,y,z);
            pushup(k);
        }
        long long query(int k,int l,int r,int x,int y)
        {
            if (x > y)
                return 0;
            if (l >= x && r <= y)
                return sm[k];
            int mid = l + r >> 1;
            pushdown(k,l,r,mid);
            if (x > mid)
                return query(yrt,mid + 1,r,x,y);
            else
                if (y <= mid)
                    return query(zrt,l,mid,x,y);
                else
                    return query(zrt,l,mid,x,y) + query(yrt,mid + 1,r,x,y);
        }
    }tree;
    struct modi
    {
        int l,r,x,v;
    }t[N * 3 + 5];
    int com(modi x,modi y)
    {
        return x.x < y.x;
    }
    int main()
    {
        scanf("%d%d%d%d",&n,&m,&p1,&p2);
        for (int i = 1;i <= n;i++)
            scanf("%d",&k[i]);
        int l,r;
        for (int i = 1;i <= m;i++)
        {
            scanf("%d%d",&l,&r);
            q[++Q_cnt].x = l - 1;
            q[Q_cnt].id = i;
            q[Q_cnt].typ = -1;
            q[Q_cnt].l = l;
            q[Q_cnt].r = r;
            q[++Q_cnt].x = r;
            q[Q_cnt].id = i;
            q[Q_cnt].typ = 1;
            q[Q_cnt].l = l;
            q[Q_cnt].r = r;
            ans[i] = (r - l) * p1;
        }
        k[n + 1] = n + 1;
        for (int i = 1;i <= n + 1;i++)
        {
            while (top && k[stk[top]] < k[i])
                R[stk[top--]] = i;
            stk[++top] = i;
        }
        top = 0;
        for (int i = n;i >= 1;i--)
        {
            while (top && k[stk[top]] < k[i])
                L[stk[top--]] = i;
            stk[++top] = i;
        }
        for (int i = 1;i <= n;i++)
        {
            if (L[i] && R[i] <= n)
                t[++m_cnt] = (modi){L[i],L[i],R[i],p1};
            if (L[i] + 1 <= i - 1 && R[i] <= n)
                t[++m_cnt] = (modi){L[i] + 1,i - 1,R[i],p2};
            if (i + 1 <= R[i] - 1 && L[i])
                t[++m_cnt] = (modi){i + 1,R[i] - 1,L[i],p2};
        }
        sort(q + 1,q + Q_cnt + 1,cmp);
        sort(t + 1,t + m_cnt + 1,com);
        int j = 1,k = 1;
        while (!q[k].x)
            k++;
        for (int i = 1;i <= n;i++)
        {
            while (t[j].x <= i && j <= m_cnt)
            {
                tree.add(1,1,n,t[j].l,t[j].r,t[j].v);
                j++;
            }
            while (q[k].x <= i && k <= Q_cnt)
            {
                ans[q[k].id] += q[k].typ * tree.query(1,1,n,q[k].l,q[k].r);
                k++;
            }
        }
        for (int i = 1;i <= m;i++)
            printf("%lld
    ",ans[i]);
        return 0;
    }
    
  • 相关阅读:
    Linux终端设置免密登陆ssh(以 XShell 为例)
    Docker入门(一)-安装
    find命令总结
    CentOS 恢复 rm -rf 误删除数据
    CentOS系统登陆root用户后发现提示符显示-bash-4.2#(已解决)
    一次在CentOS系统单用户模式下使用passwd命令破密失败的案例
    Ubuntu下配置IP地址
    安装CentOS 6.x报错"Disk sda contains BIOS RAID metadata"解决方法
    YUM命令总结
    git从安装到多账户操作一套搞定(二)多账户使用
  • 原文地址:https://www.cnblogs.com/sdlang/p/13083193.html
Copyright © 2020-2023  润新知