改了这么久,终于把这道题A了,我太弱了
感谢ZLZ巨佬的教导以及CZC帮忙调试。
来写一篇题解吧。
Description
“我希望能使用更多的魔法。不对,是预定能使用啦。最终我要被大家称呼为大魔法使。为此我决定不惜一切努力。”
——《The Grimoire of Marisa》雾雨魔理沙
魔理沙一如既往地去帕秋莉的大图书馆去借魔导书(Grimoire) 来学习魔道。
最开始的时候,魔理沙只是一本一本地进行研究。然而在符卡战中,魔理沙还是战不过帕秋莉。
好在魔理沙对自己的借还和研究结果进行了记录,从而发现了那些魔导书的精妙之处。
帕秋莉的那些魔导书,每本都有一个类别编号ti 和威力大小pi。而想要获得最有威力的魔法,就必须同时研究一些魔导书。而研究的这些魔导书就必须要满足,类别编号为T 的书的本数小于等于T,并且总共的本数小于等于一个给定的数N。而研究这些魔导书之后习得的魔法的威力就是被研究的魔导书的威力之和。
为了击败帕秋莉,魔理沙想要利用自己发现的规律来获得最有威力的魔法。
她列出了计划中之后M 次的借还事件,并想要知道每个事件之后自己所能获得的魔法的最大威力。可她忙于魔法材料——蘑菇的收集,于是这个问题就交给你来解决了。
Solution
这道题是一道很恶心的线段树,涉及到线段树的多种操作。
Pre Knowlege
1、线段树动态开点
2、线段树树上二分
3、权值线段树
首先,我们可以考虑建 t + 1 t+1 t+1棵线段树,其中第1棵到第t棵线段树,存的是编号为1~t的书本,都是权值线段树,书本编号和线段树编号一一对应。第t+1棵线段树存的是当前每一棵线段树里排名为前i个的书本有哪些?每一次先把当前书本插入或者删除从对应的线段树里,再把这棵线段树里排名为第i+1的书本插入进第t+1棵线段树或者从第t+1棵线段树删除。这样我们就有了一个大致思路。
可是这样很明显会MLE。
大家一下就可以想到,每一棵线段树用动态开点。
可是这样还是会MLE。
这里我要引用ZLZ的话:既然我们可以动态开点,为什么不可以动态开树呢?
意思就是说,我们遇到一个书本,这本书是属于之前没有用过的线段树,那么我们就新开一棵树,只不过这一棵树的根节点的编号就是当前开了的点数+1。也就像这样:
假设我们现在已经开了一棵书本编号为1的线段树,这时图长这样:
这时,我们要插入一个编号为2的书本,那么图就长这样了:
这就是所谓的动态开树。
到这里,这道题就完了。
Code
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N=3e5+5;
const int P=1e9;
int n,m,cnt=1,sum;
int root[N],tot[N];
inline int read() {
int s=0;
char ch=getchar();
while(ch<48||ch>57)ch=getchar();
while(ch>=48&&ch<=57)
s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
return s;
}