题目大意
给你M个操作,每个操作为借来或归还一本编号为Ti,威力值为Pi的魔法书。每次操作之后我们要求求当前魔法书所能弄出的最大威力。最大威力求法:最多选N本书,编号为Ti的书只能取Ti本,把他们加起来。(全是正整数)
N,M,Ti<=300,000 Pi<=
分析
这道题可以稍微把题意变一变,把要解决的问题理清晰:把每个编号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);
}
}
纵观整个程序,原型是十分失败的,这警示,不能因为“打过,觉得虽然有些生疏也没事”然后细节不想就开打了,因为打题的时候要想别的东西,如果这个时候不想,很有可能到时候就乱打一通了,到头来浪费大量时间,实在是不行。
即使题目看起来很水,也不能找实现难的借口,别人行,怎么自己就不行呢?
时间不是这么用的啊。