【NOIP2015模拟10.27】魔道研究

来源:互联网 发布:手机中小学课程表软件 编辑:程序博客网 时间:2024/06/09 13:43

题目大意

给你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);    }}

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

0 0
原创粉丝点击