bzoj 3489: A simple rmq problem

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3489
思路:
这题网上到处是树套树,我就不多说了。。。
说一下我的做法,考虑分块,假设将序列块的大小为 B ,那么对于一个询问[l,r],我们不能只保留最大的只出现一次的数,要保留的是前 2B 大的只出现一次的数,因为边界块最多有 2B2 个,它们每存在一个都有可能产生贡献或者破坏掉中间块内的答案。
怎么得到块与块之间前 2B 大小只出现一次的数呢?考虑暴力扫描,从每个块开始向后扫一直扫到序列的末尾,这样如果每次添加一个元素是均摊 O(1) 的,那么复杂度就是 O(n2B)
我们暴力扫描,维护一个权值数组,我们再权值分块,每个权值块维护出现一次的数,用一个队列维护,扫描一个块的时候若一个元素出现一次直接加入相应权值块,若已经出现则无视掉,当一个块扫描完了,从大到小扫描权值数组里的队列,把不合法的删掉,直到达到 2B 个就停止。这样对于每个元素至多会被否掉一次,均摊是 O(1)
得到块与块之间的答案后,查询的时候在数组上打标记就可以了,要适当调整块的大小。
常数大的要死。。。速度在bzoj垫底。。。感人至深。。。
复杂度: O((n+m)n

代码:

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100002
#define lim 350
#define lim1 350
using namespace std;
int n,m,a[N],b[N],ans,l,r;
    inline void in(int &x)
    {
        char c; int sy = 1;
        while (!isdigit(c = getchar()))
            if (c == '-') sy = -1;
        x = (c ^ 48);
        while (isdigit(c = getchar())) x = 10 * x + (c ^ 48);
        x *= sy;
    }

inline void init()
{
    in(n); in(m);
    for (int i = 1;i <= n; ++i) 
        in(a[i]);
    for (int i = 1;i <= n; ++i) b[i] = a[i];
    sort(b + 1,b + n + 1);
    b[0] = unique(b + 1,b + n + 1) - b - 1;
    for (int i = 1;i <= n; ++i) 
        a[i] = lower_bound(b + 1,b + b[0] + 1,a[i]) - b;
}

        int B,B1,S[lim1][N],c[N],belong[N],belong1[N],lb[N],rb[N],f[lim1][lim1][lim << 1],use[lim << 1][lim << 1];

        inline void Divide()
        {
            B = (int)(sqrt(n) * 1.1);
            belong[0] = belong[n + 1] = -1;
            for (int i = 1;i <= n; ++i) 
                belong[i] = (i - 1) / B + 1;
            for (int i = 1;i <= n; ++i)
            {
                if (belong[i] != belong[i - 1]) lb[belong[i]] = i;
                if (belong[i] != belong[i + 1]) rb[belong[i]] = i;
            }
            int t = b[0];
            B1 = (int)(sqrt(b[0]) * 1.6);
            belong1[0] = belong1[t + 1] = -1;
            for (int i = 1;i <= t; ++i) 
                belong1[i] = (i - 1) / B1 + 1;
        }

            int num,num1,ned;
            inline void Deal(int s)
            {
                for (int i = 1;i <= num1; ++i)
                    use[i][0] = 0;
                ned = (int)(B * 1.2);
                memset(S[0],0,sizeof(S[0]));
                for (int i = s;i <= num; ++i)
                {

                    for (int j = lb[i];j <= rb[i]; ++j)
                    {
                        c[a[j]]++;
                        if (c[a[j]] == 1)
                            {
                                int t = belong1[a[j]];
                                use[t][++use[t][0]] = a[j];
                            }
                    }
                    int t;
                    f[s][i][0] = 0;
                    for (int j = num1;j;--j)
                    {
                        t = 0;
                        for (int k = 1;k <= use[j][0]; ++k)
                            if (c[use[j][k]] == 1)
                            {
                                f[s][i][++f[s][i][0]] = use[j][k];
                                use[j][++t] = use[j][k];
                            }
                        use[j][0] = t;
                        if (f[s][i][0] >= ned) break;
                    }
                    if (s == 1)
                        for (int j = 1;j <= b[0]; ++j)
                            S[i][j] = c[j];
                }
                for (int i = lb[s];i <= n; ++i) c[a[i]]--;

            }

    inline void Prepare()
    {
        memset(c,0,sizeof(c));
        memset(f,0,sizeof(f));
        Divide();
        num = belong[n]; num1 = belong1[b[0]];
        for (int i = 1;i <= num; ++i) 
            Deal(i);
    }
        int Q[N];
        inline int Get_query()
        {
            int t1,t2;
            int cut = 0;
            t1 = belong[l] + 1;t2 = belong[r] - 1;
            Q[0] = 0;
            if (t1 > t2)
                for (int i = l;i <= r; ++i)
                {

                    Q[++Q[0]] = a[i];
                    cut = Q[0];
                }
            else 
                {
                    int ll = rb[belong[l]],rr = lb[belong[r]];
                    for (int i = l;i <= ll; ++i)
                        Q[++Q[0]] = a[i];
                    for (int i = rr;i <= r; ++i)
                        Q[++Q[0]] = a[i];
                        cut = Q[0];
                    for (int i = 1;i <= f[t1][t2][0]; ++i)
                        Q[++Q[0]] = f[t1][t2][i];
                }

            ans = 0;
            for (int i = 1;i <= cut; ++i) 
                c[Q[i]]++;
            for (int i = 1;i <= Q[0]; ++i)
            {
                int s = c[Q[i]];
                if (t1 <= t2) s += S[t2][Q[i]] - S[t1 - 1][Q[i]];
                if (s == 1) ans = max(ans,b[Q[i]]);
            }
            for (int i = 1;i <= cut; ++i)
                c[Q[i]]--;
        }

    inline void QUERY()
    {
        int x,y;
        ans = 0;
        for (int i = 1;i <= m; ++i)
        {
            in(x); in(y);
            l = (x + ans) % n + 1; r = (y + ans) % n + 1;
            if (l > r) swap(l,r);
            Get_query();    
            printf("%d\n",ans);
        }
    }

inline void DO_IT()
{
    Prepare();
    QUERY(); 
}

int main()
{
    //freopen("bzoj3489.in","r",stdin);
    //freopen("1.out","w",stdout);
    init();
    DO_IT();
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值