【BZOJ4241】历史研究-回滚莫队
测试地址:历史研究
做法:本题需要用到回滚莫队。
看到这道题目,感觉主席树、树套树这种都不能做,于是考虑离线的莫队算法。
然而我们很快发现一个问题:对于题目中要求的信息,由于在插入时,值要么不变,要么变成你目前插入的元素所对应的重要度,所以这个很容易完成。但是删除时就不一定了,你并不知道值此时会怎么变化。这就意味着我们不能用传统的莫队算法解决这一问题,而需要使用一种叫回滚莫队的方法规避删除操作。
回滚莫队中对询问排序的方法和传统莫队仍然一样,都是以左端点所在的块为第一关键字,以右端点为第二关键字排序。接下来我们分类讨论:
首先,对于左右端点在同一块内的询问,显然可以直接求出答案。
然后,对于左右端点不在同一块内的询问,我们每次处理左端点在当前块内的询问。因为左右端点不在同一块中,因此区间会被当前块的右边界分割成两段。又因为右端点已经是有序的了,因此我们可以轻易计算右边那段的信息,显然对于每一块最多插入次。而对于每个询问,我们直接进行插入来计算整段的信息(从右边段开始扩展)。处理完这个询问后,就把往左的插入操作依次撤销(也就是“回滚”),回到只剩下右边段信息的状态,因此每个询问回滚的复杂度是的。这样总的时间复杂度还是,但规避了删除操作,于是利用这个思想就可以解决这一题了。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,tot,a[100010],blocksiz;
ll val[100010],cnt[100010]={0},excnt[100010]={0},ans[100010];
struct forsort
{
int id;
ll val;
}f[100010];
struct Query
{
int l,r,id;
}q[100010];
bool cmp(forsort a,forsort b)
{
return a.val<b.val;
}
bool cmpq(Query a,Query b)
{
if (a.l/blocksiz!=b.l/blocksiz)
return a.l/blocksiz<b.l/blocksiz;
else return a.r<b.r;
}
void Mo()
{
blocksiz=(int)(sqrt(n)+1);
sort(q+1,q+m+1,cmpq);
int now=0,l,r=(now+1)*blocksiz-1;
ll mx=0;
for(int i=1;i<=m;i++)
{
if (now<q[i].l/blocksiz)
{
now=q[i].l/blocksiz;
mx=0,r=(now+1)*blocksiz-1;
memset(cnt,0,sizeof(cnt));
}
if (q[i].l/blocksiz==q[i].r/blocksiz)
{
l=q[i].l,r=l-1;
ans[q[i].id]=0;
while(r<q[i].r)
{
r++;
cnt[a[r]]++;
ans[q[i].id]=max(ans[q[i].id],val[a[r]]*cnt[a[r]]);
}
r=l-1;
while(r<q[i].r) r++,cnt[a[r]]--;
r=(now+1)*blocksiz-1;
}
else
{
l=(now+1)*blocksiz;
while(r<q[i].r)
{
r++;
cnt[a[r]]++;
mx=max(mx,val[a[r]]*cnt[a[r]]);
}
ans[q[i].id]=mx;
while(l>q[i].l)
{
l--;
excnt[a[l]]++;
ans[q[i].id]=max(ans[q[i].id],val[a[l]]*(cnt[a[l]]+excnt[a[l]]));
}
l=(now+1)*blocksiz;
while(l>q[i].l) l--,excnt[a[l]]--;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&f[i].val);
f[i].id=i;
}
sort(f+1,f+n+1,cmp);
tot=0;
for(int i=1;i<=n;i++)
{
if (i==1||f[i].val!=f[i-1].val)
val[++tot]=f[i].val;
a[f[i].id]=tot;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
Mo();
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}