bzoj4241 歷史研究 回滾莫隊

題目大意:
有一個長度爲n的序列。
有m個詢問,每次詢問l~r範圍內每個數值乘以該數值出現次數的最大值。

題目分析:
據說這題可以在線做?

這題普通的莫隊GG,因爲不支持快速刪除操作,但是支持快速加入一個值的操作,所以上回滾莫隊就好了。

回滾莫隊可以把刪除操作去掉,並且時間複雜度仍然保持在在O(nsqrtn)。

分塊和排序都按照正常莫隊做法來,然後在統計答案的時候,如果一個詢問的左端點和右端點在同一個塊內,那就暴力統計。
然後對於左端點在同一塊內的詢問我們一起統計,首先讓左端點在這一塊的最右端,然後讓右端點正常向右擴張。當右端點滿足條件的時候,記錄一下這個時候的狀態,再把左端點調整到詢問的左端點,這個時候統計一下答案,然後再回滾到沒有調整左端點的時候的那個狀態。然後再做下一個詢問就可以了。

這樣仍然保證了時間複雜度是O(nsqrtn)

代碼如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define N 120000
using namespace std;
typedef long long LL;
inline LL Max(LL x,LL y) { return x>y?x:y; }
int n,m,block_size,pos;
int a[N],d[N],belong[N],top;
LL sum[N],tmp[N],last_ans,cur_ans,ans[N];
struct Query{
    int l,r,id;
    bool operator < (const Query &c) const { return belong[l]<belong[c.l] || (belong[l]==belong[c.l] && r<c.r); }
}q[N];
void Add(int c)
{
    sum[c]+=d[c];
    cur_ans=Max(cur_ans,sum[c]);
}
void Del(int c)
{
    sum[c]-=d[c];
}
int main()
{
    scanf("%d%d",&n,&m);
    block_size=sqrt(n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),d[i]=a[i];
    sort(d+1,d+1+n);
    top=unique(d+1,d+1+n)-d;
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(d+1,d+top,a[i])-d;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    for(int i=1;i<=n;i++)
        belong[i]=(i-1)/block_size+1;
    sort(q+1,q+1+m);
    int l,r;
    for(int i=1;i<=m;i++)
    {
        if(belong[q[i].l]!=belong[q[i-1].l])
        {
            memset(sum,0,sizeof(sum));
            last_ans=cur_ans=0;
            l=pos=belong[q[i].l]*block_size+1;
            r=l-1;
        }
        if(belong[q[i].l]==belong[q[i].r])
        {
            LL cur=0;
            for(int j=q[i].l;j<=q[i].r;j++)
                tmp[a[j]]+=d[a[j]],cur=Max(cur,tmp[a[j]]);
            for(int j=q[i].l;j<=q[i].r;j++)
                tmp[a[j]]-=d[a[j]];
            ans[q[i].id]=cur;
            continue;
        }
        while(r<q[i].r) Add(a[++r]);
        last_ans=cur_ans;
        while(l>q[i].l) Add(a[--l]);
        ans[q[i].id]=cur_ans;
        while(l<pos) Del(a[l++]);
        cur_ans=last_ans;
    }
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章