zoukankan      html  css  js  c++  java
  • AT1219 歴史の研究 回滚莫队

    可在vj上提交:https://vjudge.net/problem/AtCoder-joisc2014_c

    题意:

    IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。

    日记中记录了连续 NN 天发生的时间,大约每天发生一件。

    事件有种类之分。第 ii 天发生的事件的种类用一个整数 X_iXi 表示,X_iXi 越大,事件的规模就越大。

    JOI 教授决定用如下的方法分析这些日记:

    • 选择日记中连续的几天 [L,R][L,R] 作为分析的时间段;

    • 定义事件 AA 的重要度 W_AWA 为 A imes T_AA×TA,其中 T_ATA 为该事件在区间 [L,R][L,R] 中出现的次数。

    现在,您需要帮助教授求出所有事件中重要度最大的事件是哪个,并输出其重要度。

    注意:教授有多组询问。

    输入格式

    第一行两个空格分隔的整数 NN 和 QQ,表示日记一共记录了 NN 天,询问有 QQ 次。

    接下来一行 NN 个空格分隔的整数表示每天的事件种类。

    接下来 QQ 行,每行给出 L,RL,R 表示一组询问。

    输出格式

    输出共有 QQ 行,每行一个整数,表示对应的询问的答案。

    数据范围

    对于 100\%100% 的数据,1le Q,Nle 10^51Q,N105,1le Xle 10^91X109,1le Lle Rle 10^51LR105。

    输入输出样例

    输入 #1
    5 5
    9 8 7 8 9
    1 2
    3 4
    4 4
    1 4
    2 4
    输出 #1
    9
    8
    8
    16
    16

    题解:

    我们首先说一下普通莫队怎么做:
    首先就是对区间排序,因为我们需要找一个区间内的最大值,这个我们可以用以变量tmp来记录。
    如果要添加一个数,那么我们用cnt数组来记录某个数出现的次数,并更新tmp的值
    但是如果要删除一个数,虽然我们的cnt数组还可以通过减去1来保证cnt数组正确性,但是我们没有办法去更新tmp
    那么要是保证tmp正确性,我们就必须对这个区间重新遍历一次,而不能在上一个区间的前提下操作。
    那么如果这样操作的话,复杂度就是n*n*sqrt(n),相信这个复杂度大多题目都会TLE了

    回滚莫队解决它:
    首先排序函数要改一下,如果两个查询区间的左边界不在一个块,那就按照左边界从小到大排序,否则那就按照右边界从小到大排序

    ll cmp(query a, query b)
    {
        return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : a.r < b.r;
    }

    对于一个查询区间[l,r],我们把l叫做左边界,把r叫做右边界。使用左标记和右标记来在n个数的位置上移动

    这样,对于左边界属于一个块(我们把n个数分成sqrt(n)个块)的查询区间,因为这些查询区间的右边界是递增的,所以右标记就只会向右移动,不会向左,就意味着不会出现删除元素的情况


    但是这些查询区间的左边界可不一定和右边界的位置对应,也就是说对于右边界添加元素和删除元素的情况都会出现,怎么解决呢?
    我们就暴力去解决,每次把左标记移动到一个查询区间的左边界,操作完之后在移动原位置(这一点可以看代码)

    for(; belong[que[i].l]==k; ++i) //在同一个块内
            {
                ll start=que[i].l,last=que[i].r;
                ll tmp;
                while(r<last)  //因为我们的排序函数,所以我们只需要r<last和l>start两种情况
                {
                    ++r;
                    ++cnt[typ[r]];
                    now=max(now,1ll*cnt[typ[r]]*arr[r]);
                }
                tmp=now;
                while(l > start)  //将左标记回归到原位置
                {
                    --l;
                    ++cnt[typ[l]];
                    now = max(now, 1ll * cnt[typ[l]] * arr[l]);
                }
    
                ans[que[i].id] = now;
                while(l < rb[k] + 1)
                {
                    --cnt[typ[l]];
                    l++;
                }
                now = tmp;
            }

    这个复杂度就是n*sqrt(n)。因为每次左标记都是一个固定位置,我们移动到一个查询区间的左边界这个复杂度就是sqrt(n)
    因为一共有m个查询,所以m个边界(题目上说了m<=n) 所以最大复杂度就是n*sqrt(n)


    然后右边界可能不在这个块内,但是右标记不需要来回移动,它只需要一直向右移动(添加元素),所以复杂度n
    因为最多也就sqrt(n)个块,所以复杂度最大就是n*sqrt(n)

    所以复杂度也就是n*sqrt(n)

    代码:

    #include <map>
    #include <set>
    #include <list>
    #include <queue>
    #include <deque>
    #include <cmath>
    #include <stack>
    #include <vector>
    #include <bitset>
    #include <cstdio>
    #include <string>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int maxn = 2e5+10;
    const int INF = 0x3f3f3f3f;
    const double PI = 3.1415926;
    const long long N = 1000006;
    const double eps = 1e-10;
    typedef long long ll;
    #define mem(A, B) memset(A, B, sizeof(A))
    #define lson rt<<1 , L, mid
    #define rson rt<<1|1 , mid + 1, R
    #define ls rt<<1
    #define rs rt<<1|1
    #define SIS std::ios::sync_with_stdiget_mod_new(z-x)o(false), cin.tie(0), cout.tie(0)
    #define pll pair<long long, long long>
    #define lowbit(abcd) (abcd & (-abcd))
    #define max(a, b) ((a > b) ? (a) : (b))
    #define min(a, b) ((a < b) ? (a) : (b))
    inline ll read()    //读取整数
    {
        ll res = 0;
        char c = getchar();
        while(!isdigit(c)) c = getchar();
        while(isdigit(c)) res = (res << 1) + (res << 3) + (c ^ 48), c = getchar();
        return res;
    }
    ll arr[maxn],cnt[maxn],cnt2[maxn],typ[maxn],inp[maxn],belong[maxn],lb[maxn],rb[maxn];
    ll n,m,sizes,new_size;
    ll ans[maxn];
    struct query
    {
        ll l,r,id;
    } que[maxn];
    ll cmp(query a, query b)
    {
        return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : a.r < b.r;
    }
    int main()
    {
        n=read(),m=read();
        sizes=sqrt(n);
        new_size=ceil((double)n/sizes);
        for(ll i = 1; i <= new_size; ++i)
        {
            lb[i] = sizes * (i - 1) + 1;
            rb[i] = sizes * i;
            for(ll j = lb[i]; j <= rb[i]; ++j)
                belong[j] = i;
        }
        rb[new_size]=n;
        for(ll i=1; i<=n; ++i)
            arr[i]=inp[i]=read();
        sort(inp+1,inp+1+n);
        ll tot=unique(inp+1,inp+1+n)-inp-1;
        for(ll i=1; i<=n; ++i)
        {
            typ[i]=lower_bound(inp+1,1+inp+tot,arr[i])-inp;
        }
        for(ll i=1; i<=m; ++i)
        {
            que[i].l=read();
            que[i].r=read();
            que[i].id=i;
        }
        sort(que+1,que+1+m,cmp);
        ll i=1;
        for(ll k=1; k<=new_size; ++k)
        {
            ll l=rb[k]+1,r=rb[k];
            ll now=0;
            mem(cnt,0);
            for(; belong[que[i].l]==k; ++i) //在同一个块内
            {
                ll start=que[i].l,last=que[i].r;
                ll tmp;
                if(belong[start]==belong[last])  //特判
                {
                    tmp=0;
                    for(ll j=start;j<=last;++j)
                    {
                        cnt2[typ[j]]=0;
                    }
                    for(ll j=start; j<=last; ++j)
                    {
                        cnt2[typ[j]]++;
                        tmp=max(tmp,1ll*cnt2[typ[j]]*arr[j]);
                    }
                    ans[que[i].id]=tmp;
                    continue;
                }
                while(r<last)  //因为我们的排序函数,所以我们只需要r<last和l>start两种情况
                {
                    ++r;
                    ++cnt[typ[r]];
                    now=max(now,1ll*cnt[typ[r]]*arr[r]);
                }
                tmp=now;
                while(l > start)
                {
                    --l;
                    ++cnt[typ[l]];
                    now = max(now, 1ll * cnt[typ[l]] * arr[l]);
                }
    
                ans[que[i].id] = now;
                while(l < rb[k] + 1)
                {
                    --cnt[typ[l]];
                    l++;
                }
                now = tmp;
            }
    
        }
        for(ll i=1; i<=m; ++i) printf("%lld
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    CentOS7 安装 Mysql5.6.40
    CentOS7 安装 Python3.6.5
    CentOS7 添加新用户并授权 root 权限
    02 常用数据结构
    01 常见算法
    初识 Docker
    glob & fnmatch -- 使用Unix style通配符
    Pillow6 起步
    leetcode70. 爬楼梯 🌟
    leetcode69. x 的平方根 🌟
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/13722522.html
Copyright © 2011-2022 走看看