【NOIP2015模拟10.27】魔道研究

时间:2022-12-17 12:52:46

题目大意

给你M个操作,每个操作为借来或归还一本编号为Ti,威力值为Pi的魔法书。每次操作之后我们要求求当前魔法书所能弄出的最大威力。最大威力求法:最多选N本书,编号为Ti的书只能取Ti本,把他们加起来。(全是正整数)
N,M,Ti<=300,000 Pi<= 109

分析

这道题可以稍微把题意变一变,把要解决的问题理清晰:把每个编号Ti的书的威力值前Ti大的书放到一个集合S里,把此集合里前N大的数加起来。
所以用T+1颗线段树维护每个编号及S集合,下标为Pi,动态开区间,用一个数组弄起来。

AC后总结

真是艰难啊,先是想弄一堆堆和2线段树,编程复杂度太大,然而并没有技术含量,我还傻傻的不估计编程复杂度,比赛打到一半弃疗。
这类题都打过,改起来还是辛苦,很多之前出过的错又上来了。错误贴在代码上

#include<cstdio>
#include<algorithm>
#include<iostream>
#define fo(i,j,k) for(i=j;i<=k;i++)
#define ll long long 
using namespace std;

const ll N=400005,p=1000000005;
struct rec
{
    ll l,r,cnt,val;
}b[30000000];
struct re
{
    ll pos,val;
};

ll st[N],sst,i,j,dur,ans,n,m,x,y,tt;
char ch[20];
ll change(ll x,ll l,ll r,ll pos,ll val)
{
    if (!x||!pos) return 0;
    if (l==r)
    {
        b[x].cnt+=val;
        b[x].val=b[x].cnt*l;
        //一开始已经知道一位的书本数不止一本,然而val却没有*l,都不知道怎么搞的
        return 0;
    }
    ll m=(l+r)/2;
    if (m>=pos)
    {
        if (!b[x].l) b[x].l=++tt;
        change(b[x].l,l,m,pos,val);
    }
    else
    {
        if (!b[x].r) b[x].r=++tt;
        change(b[x].r,m+1,r,pos,val);
    }
    b[x].cnt=b[b[x].l].cnt+b[b[x].r].cnt;
    b[x].val=b[b[x].l].val+b[b[x].r].val;
    return 0;
}

ll get(ll x,ll l,ll r,ll i,ll j)
{
    if (x==0) return 0;
    if (l==i&&r==j)
        return b[x].cnt;
    ll m=(l+r)/2;
    if (m>=j)
        return get(b[x].l,l,m,i,j);
    else
    if (m<i)
        return get(b[x].r,m+1,r,i,j);
    else 
        return (get(b[x].l,l,m,i,m)+
                get(b[x].r,m+1,r,m+1,j));
}
re get1(ll x,ll l,ll r,ll xth)
{
    re dur={0,0};
    if (xth==0) return dur;
    if (xth>b[x].cnt)
    {
        dur.pos=0;
        dur.val=b[x].val;
        return dur;
    }
    if (l==r)
    {
        dur.val=xth*l;
        dur.pos=l;
        //完全忘记Xth到这个时候有可能不止1,我之前就打的dur.val=l,然后return,之后改的时候又没想清楚,给.val=l乘b[x].cnt,真是炸了,最后才改对。
    }
    ll m=(l+r)/2;
    if (b[b[x].r].cnt>=xth)
        return get1(b[x].r,m+1,r,xth);
    else
    {
        dur=get1(b[x].l,l,m,xth-b[b[x].r].cnt);
        dur.val+=b[b[x].r].val;
        return dur;
    }
}
int main()
{
    freopen("grimoire.in","r",stdin);
    freopen("grimoire.out","w",stdout);
    scanf("%lld %lld",&n,&m);
    fo(i,1,m)
    {
        if (i==241816)
        {
            i=i;//调戏用
        }
        scanf("%s %lld %lld",ch+1,&x,&y);
        if (ch[1]=='B')
        {
            if (st[x]==0)
                st[x]=++tt;
            if (get(st[x],1,p,y,p)<x)
            {
                if (sst==0)
                    sst=++tt;
                change(st[x],1,p,y,1);
                dur=get1(st[x],1,p,x+1).pos;//the x+1 th
                change(sst,1,p,y,1);
                change(sst,1,p,dur,-1);
            }
            else
                change(st[x],1,p,y,1);
        }
        else
        {
            if (get(st[x],1,p,y,p)<=x)
            {
                change(st[x],1,p,y,-1);
                dur=get1(st[x],1,p,x).pos;
                change(sst,1,p,y,-1);
                change(sst,1,p,dur,1);
            }
            else 
                change(st[x],1,p,y,-1);
        }
        dur=get1(sst,1,p,n).val;//一开始所有线段树居然全都维护前X小的了,然而是要降序啊,然后我改了一半成降序的,另一半还是升序,后面耗了好久才改对
        printf("%lld\n",get1(sst,1,p,n).val);
    }
}

纵观整个程序,原型是十分失败的,这警示,不能因为“打过,觉得虽然有些生疏也没事”然后细节不想就开打了,因为打题的时候要想别的东西,如果这个时候不想,很有可能到时候就乱打一通了,到头来浪费大量时间,实在是不行。
即使题目看起来很水,也不能找实现难的借口,别人行,怎么自己就不行呢?
时间不是这么用的啊。