zoukankan      html  css  js  c++  java
  • BZOJ4241:历史研究(回滚莫队)

    题意:给定N个数字,Q次询问,询问这个区间的最大加权众数是多少。 加权众数是指出现次数*数字大小。N,Q<1e5。

    思路:不难发现可以N*sqrtN*logN的思路做,但是应该过不了。 这个Nsqrt是莫队的时间,log的支持加入和删除的数据结构的复杂度。 如果用配对堆的话,应该还是比较快的。 没有用办法去掉这个log呢。

        回滚莫队:

    排序:按照左端点所在块排序,如果左端点在同一块,则按照右端点大小排序。 

    分类处理:然后左端点在同一块的同时处理,(sqrt)次。我们维护一个num[],表示每个数字出现的次数。

    1,对于他们的右端点,这些询问的右端点是单调递增的。所以每次直接右移即可。复杂度O(N)*sqrt;

    2,但是左端点参差不齐,没关系,我们把他们的左端点都设置为L右边那一块的起点。  这样我们每次暴力处理左边部分加进去即可,更新答案后又还原。 这样就保证了只加不减,可以直接O(1)取最大了。O(sqrt)*M。

    优点:保证了只加入元素,这样在诸如求“众数”,“mex”这一类题中不需要其他的数据结构。

    当然,在求其他问题,比如区间逆序对时,我们的“回滚”好像鞭长莫及,这个时候可以用“二次离线莫队”来做。

    #include<bits/stdc++.h>
    #define int long long
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=200010;
    struct in{
        int L,R,bg,id;
    }s[maxn];
    bool cmp(in w,in v){
        if(w.bg==v.bg) return w.R<v.R;
        return w.L<v.L;
    }
    ll ans[maxn]; int a[maxn],b[maxn],num[maxn],tot,B,N,Q;
    void bruteforce(int i)
    {
        rep(j,s[i].L,s[i].R){
            num[a[j]]++;
            ans[s[i].id]=max(ans[s[i].id],1LL*num[a[j]]*b[a[j]]);
        }
        rep(j,s[i].L,s[i].R) num[a[j]]--;
    }
    void solve(int l,int r)
    {
        int fcy=min(s[l].bg*B,N),now=fcy,mx=0,tmp=0;
        while(l<=r&&s[l].R<=fcy) bruteforce(l++);
        for(;l<=r;l++){
            while(now<s[l].R) {
                now++; num[a[now]]++;
                mx=max(mx,1LL*num[a[now]]*b[a[now]]);
                ans[s[l].id]=mx;
            }
            tmp=mx;
            for(int j=fcy;j>=s[l].L;j--) {
                num[a[j]]++;
                tmp=max(tmp,1LL*num[a[j]]*b[a[j]]);
                ans[s[l].id]=tmp;
            }
            for(int j=fcy;j>=s[l].L;j--) num[a[j]]--;
        }
        for(int i=fcy+1;i<=now;i++) num[a[i]]=0;
    }
    signed main()
    {
        scanf("%lld%lld",&N,&Q); B=sqrt(N+1);
        rep(i,1,N) scanf("%lld",&a[i]),b[i]=a[i];
        sort(b+1,b+N+1);
        tot=unique(b+1,b+N+1)-(b+1);
        rep(i,1,N) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
        rep(i,1,Q) {
            scanf("%lld%lld",&s[i].L,&s[i].R);
            s[i].id=i; s[i].bg=(s[i].L-1)/B+1;
        }
        sort(s+1,s+Q+1,cmp);
        rep(i,1,Q){
            int j=i;
            while(j+1<=Q&&s[j+1].bg==s[i].bg) j++;
            solve(i,j);
            i=j;
        }
        rep(i,1,Q) printf("%lld
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    VB6SP6极度精简兼容绿色版
    Upnp资料整理
    RevMan简单入门指南
    小程序 --flex
    IV
    2017-10-27错误日志
    170616_2
    170616
    2017-06-07
    111111112222
  • 原文地址:https://www.cnblogs.com/hua-dong/p/11574556.html
Copyright © 2011-2022 走看看