zoukankan      html  css  js  c++  java
  • [NOIP2015模拟10.27] [JZOJ4270] 魔道研究 解题报告(动态开点+权值线段树上二分)

    Description

    “我希望能使用更多的魔法。不对,是预定能使用啦。最终我要被大家称呼为大魔法使。为此我决定不惜一切努力。”
    ——《The Grimoire of Marisa》雾雨魔理沙
    魔理沙一如既往地去帕秋莉的大图书馆去借魔导书(Grimoire) 来学习魔道。
    最开始的时候,魔理沙只是一本一本地进行研究。然而在符卡战中,魔理沙还是战不过帕秋莉。
    好在魔理沙对自己的借还和研究结果进行了记录,从而发现了那些魔导书的精妙之处。
    帕秋莉的那些魔导书,每本都有一个类别编号ti 和威力大小pi。而想要获得最有威力的魔法,就必须同时研究一些魔导书。而研究的这些魔导书就必须要满足,类别编号为T 的书的本数小于等于T,并且总共的本数小于等于一个给定的数N。而研究这些魔导书之后习得的魔法的威力就是被研究的魔导书的威力之和。
    为了击败帕秋莉,魔理沙想要利用自己发现的规律来获得最有威力的魔法。
    她列出了计划中之后M 次的借还事件,并想要知道每个事件之后自己所能获得的魔法的最大威力。可她忙于魔法材料——蘑菇的收集,于是这个问题就交给你来解决了。

    Input

    输入文件grimoire.in。
    第1 行2 个整数N,M,分别表示魔理沙能研究的魔导书本数的上限和她的借还事件数。
    之后M 行,每行的形式为“op t p”(不含引号)。Op 为“BORROW” 或“RETURN”,分别表示借书和还书。T 为一个整数,表示这本书的类别编号。P为一个整数,表示这本书的威力大小。注意,还书时如果有多本书满足类别编号为t,威力大小为p,这表明这些书都是相同的,魔理沙会任选其中一本书还回去。如果你问我为何会有相同的书,多半因为这是魔导书吧。

    Output

    输出文件grimoire.out。
    一共M 行,每行一个整数,即每个事件之后的最大威力。

    Sample Input

    5 10 
    BORROW 1 5811 
    BORROW 3 5032
    RETURN 3 5032 
    BORROW 3 5550 
    BORROW 5 3486 
    RETURN 1 5811 
    RETURN 3 5550 
    BORROW 4 5116 
    BORROW 3 9563 
    BORROW 5 94

    Sample Output

    5811
    10843
    5811
    11361
    14847
    9036
    3486
    8602
    18165
    18259
     

    Data Constraint

    对于5% 的数据,1 <= t,N,M <= 50。
    对于10% 的数据,1 <= t,N,M <= 100。
    对于30% 的数据,1 <= t,N,M<= 10 000。
    另有30% 的数据,1 <= p <= 1 000。
    对于100% 的数据,1 <= t,N,M <= 300 000,1<= p<= 1 000 000 000。
    另外,总共有30% 的数据,满足没有“RETURN” 操作。这部分数据均匀分布。

    题目大意:给定t个集合,我们要维护任意区间加入和删除元素,并且把第q区间前q大的元素放在一起维护一个新的集合的前n大

    “我们有若干个可重集合,然后我们从第 i 个可重集合中拿前 i 大组成一个新的可重集合 S。我们的目的是动态维护 S 的前 n 大的和。--来自题解

    好的,集合第k大的感觉,一开始怀疑是主席树,但是并不是

    题解:

    显然,我们需要维护所有的小集合以及 S。 维护 maxt 棵权值线段树,第 i 棵线段树对应第 i 个小集合。 我们称这些线段树为小树。 另外维护一棵权值线段树,用来维护 S。我们称这棵线段树为大树。对于 BORROW 操作,我们就是在第 t 棵小树中加入一个元素 p。这个可以通过线段树单点修改完成。 之后,我们要确定要不要将 p 加入 S 中。这个只要知道 p 在第 t 个集合中的排名是否 ≤ t 即可。 加入了 S 以后,我们还要将原来的第 t 大,即现在的第 t+1 大从 S 中删去。对于 RETURN 操作,我们可以类似地,将 BORROW 操作取反即可。而动态维护S前n大的和自然也是小菜一碟了。

    需要注意的细节:

    ①我们区间查找的始终是从大到小的,因此我们在权值线段树上二分的时候要先比较右节点

    ②val数组维护的是和,而权值在叶子节点上

    ③权值线段树的空间需求是不能接受的,但动态开点可以解决这个问题

    ④不要用cin读入字符串,笔者在这儿足足卡了一个小时TLE

    ⑤S的根节点不能叫n+1,这是一个坑,题目没有说是我浪了,一直20分

    官方题解:https://jzoj.net/senior/index.php/main/download/4270/slide.pdf/0/solution_path,不知道能不能打开

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    const int maxn=300010;
    const int Y=1000000050;
    int n,m,sz,tr;
    ll val[maxn*90];
    int tree[maxn],sum[maxn*90];
    char c[20];
    struct TREE
    {
        int l;int r;
    }t[maxn*90];
    inline int read()
    {
        char ch=getchar();
        int s=0,f=1;
        while (!(ch>='0'&&ch<='9')) {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void update(int &rt,int l,int r,int pos,int z)
    {
        if (!rt) rt=++sz;
        if (!pos) return;
        if (l==r)
        {    
            sum[rt]+=z;
            val[rt]=sum[rt]*l;
            return;
        }
        int mid=l+r>>1;
        if (pos<=mid) update(t[rt].l,l,mid,pos,z);
        else update(t[rt].r,mid+1,r,pos,z);
        sum[rt]=sum[t[rt].l]+sum[t[rt].r];
        val[rt]=val[t[rt].l]+val[t[rt].r];
        return;
    }
    ll query(int &rt,int l,int r,int ls,int rs)
    {
        if (!rt) {rt=++sz;return 0;}
        if (l>=ls&&r<=rs) return sum[rt];
        int mid=l+r>>1;
        int qq=0;
        if (ls<=mid) qq+=query(t[rt].l,l,mid,ls,rs);
        if (rs>mid) qq+=query(t[rt].r,mid+1,r,ls,rs);
        return qq;
    }
    int get1(int &rt,int l,int r,int xth)//从大到小第xth个数是多少 pos
    {
        if (!rt) rt=++sz;
        if (!xth) return 0;
        if (xth>sum[rt]) return 0; 
        if (l==r) return l;
        int mid=l+r>>1; 
        if (xth<=sum[t[rt].r]) return get1(t[rt].r,mid+1,r,xth);
        else return get1(t[rt].l,l,mid,xth-sum[t[rt].r]);
    }
    ll get2(int &rt,int l,int r,int xth)//后xth个数的值是多少 val
    {
        if (!rt) rt=++sz;
        if (!xth) return 0;
        if (xth>sum[rt]) return val[rt];
        if (l==r) return xth*l*1ll;
        int mid=l+r>>1;
        if (xth<=sum[t[rt].r]) return get2(t[rt].r,mid+1,r,xth);
        else return get2(t[rt].l,l,mid,xth-sum[t[rt].r])+val[t[rt].r];
    }
    int main()
    {
        freopen("grimoire.in","r",stdin);
        freopen("grimoire.out","w",stdout); 
        n=read();m=read();
        for (int i=1;i<=m;i++)
        {
            scanf("%s",c);
            int t=read(),p=read();
            if (c[0]=='B'||c[1]=='B'||c[2]=='B')
            {
                ll k=query(tree[t],1,Y,p,Y);
                if (k<t)
                {
                    update(tree[t],1,Y,p,1);
                    int pos=get1(tree[t],1,Y,t+1);
                    update(tr,1,Y,p,1);
                    update(tr,1,Y,pos,-1);
                }
                else update(tree[t],1,Y,p,1);
            }
            else
            {
                ll k=query(tree[t],1,Y,p,Y);
                if (k<=t)
                {
                    update(tree[t],1,Y,p,-1);
                    int pos=get1(tree[t],1,Y,t);
                    update(tr,1,Y,p,-1);
                    update(tr,1,Y,pos,1);
                }
                else update(tree[t],1,Y,p,-1);
            }
            ll ans=get2(tr,1,Y,n);
            printf("%lld
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    Centos warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory
    gitlab安装与配置(Centos6.8)
    重装MAC系统 “安装器有效负载签名检查失败” 解决方法
    nginx配置限制同一个ip的访问频率
    nginx配置文件详解
    C# 获取打印机列表以及串口
    JQ将数组转换为Json
    快速查看SQL Server 中各表的数据量以及占用空间大小
    键盘unicode值对照表
    JavaScript经典代码【二】【javascript判断用户点了鼠标左键还是右键】
  • 原文地址:https://www.cnblogs.com/xxzh/p/9299789.html
Copyright © 2011-2022 走看看