• 「题解」:[BZOJ4358]permu


    问题: permu

    时间限制: 30 Sec  内存限制: 512 MB

    题面


    题目描述

    给出一个长度为n的排列P(P1,P2,...Pn),以及m个询问。每次询问某个区间[l,r]中,最长的值域

    连续段长度。

    输入格式

    第一行两个整数n,m。

    接下来一行n个整数,描述P。

    接下来m行,每行两个整数l,r,描述一组询问。

    输出格式

    对于每组询问,输出一行一个整数,描述答案。

    样例输入

    8 3
    3 1 7 2 5 8 6 4
    1 4
    5 8
    1 7

    样例输出

    3
    3
    4

    题解


     

    线段树+莫队。

    考虑维护区间连续值域长度最大值。

    首先显然莫队。(毕竟是蒟蒻我在莫队专题里遇到的题目……

    序列操作、多次询问,最关键的是无修!(没学过带修莫队不行啊 /理直气壮)

    于是考虑怎么在不同区间内转移。

    我们先种一棵值域线段树维护点的存在与否。

    我们假设li为某段区间从左边界开始的最长值域连续段,ri为从右边界开始的最长值域连续段,mi为区间中最长值域连续段,考虑如何转移。

    当push_up的时候,li由左儿子的li转移而来。特殊地,我们考虑左儿子整个区间连续,则li由左儿子的区间大小加上右儿子的li转移而来。

    同理,ri由右儿子的ri转移。特殊情况为右儿子的区间大小加上左儿子的ri。

    考虑mi的转移。显然mi由左儿子的mi和右儿子的mi以及左儿子的ri与右儿子的li组成的新区间转移而来。

    注意我们开的是值域线段树,因此可以保证区间的连续性。

    于是线段树维护3个标记我们就可以方便的进行转移了。

    然后就是莫队板子。不断在线段树中插入点,删除点并进行转移统计答案。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define rint register int
    using namespace std;
    int n,m,a[50004],tk[50004],belong[50004],l,r,sum_q,ans[50004];
    struct node1{int li,ri,mi,size;}t[800005];
    struct node2{int l,r,id;}que[50004];
    inline bool cmp(node2 a,node2 b)
    {
        return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.l]&1)?a.r<b.r:a.r>b.r);
    }
    inline void build(rint k,rint l,rint r)
    {
        if(l==r){t[k].size=1;return ;}
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        t[k].size=t[k<<1].size+t[k<<1|1].size;
        return ;
    }
    inline void update(rint k)
    {
        t[k].li=t[k<<1].li;t[k].ri=t[k<<1|1].ri;
        if(t[k<<1].li==t[k<<1].size)t[k].li+=t[k<<1|1].li;
        if(t[k<<1|1].ri==t[k<<1|1].size)t[k].ri+=t[k<<1].ri;
    //    cout<<"left:"<<t[k<<1].mi<<" right:"<<t[k<<1|1].mi<<" mid:"<<t[k<<1].ri+t[k<<1|1].li<<endl;
        t[k].mi=max(max(t[k<<1].mi,t[k<<1|1].mi),t[k<<1].ri+t[k<<1|1].li);
    }
    inline void change(rint k,rint p,rint u,rint l,rint r)
    {
    //    cout<<"change:"<<p<<endl;
        if(l==u&&r==u)
        {
    //        cout<<p<<endl;
            t[k].mi=p;t[k].li=p;t[k].ri=p;
    //        cout<<"mi:"<<t[k].mi<<endl;
            return ;
        }
        rint mid=(l+r)>>1;
        if(u<=mid)change(k<<1,p,u,l,mid);
        else change(k<<1|1,p,u,mid+1,r);
        update(k);//cout<<t[k].mi<<endl;
        return ;
    }
    int main()
    {
        scanf("%d %d",&n,&m);
        sum_q=(int)sqrt(n);
        for(rint i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            belong[i]=i/sum_q+1;
        }
        build(1,1,n);
        for(rint i=1;i<=m;++i)
        {
            scanf("%d %d",&que[i].l,&que[i].r);
            que[i].id=i;
        }
        sort(que+1,que+m+1,cmp);
        for(rint i=que[1].l;i<=que[1].r;++i)
        {
            tk[a[i]]++;
            if(tk[a[i]]==1)change(1,1,a[i],1,n);
        }
        ans[que[1].id]=t[1].mi;
        l=que[1].l,r=que[1].r;
        for(rint i=2;i<=m;++i)
        {
    //        cout<<l<<" "<<que[i].l<<endl;
            while(l<que[i].l){tk[a[l]]--;if(tk[a[l]]==0)change(1,0,a[l],1,n);l++;}
    //        cout<<l<<" "<<que[i].l<<endl;
            while(l>que[i].l){l--;tk[a[l]]++;if(tk[a[l]]==1)change(1,1,a[l],1,n);}
            while(r<que[i].r){r++;tk[a[r]]++;if(tk[a[r]]==1)change(1,1,a[r],1,n);}
            while(r>que[i].r){tk[a[r]]--;if(tk[a[r]]==0)change(1,0,a[r],1,n);r--;}
            ans[que[i].id]=t[1].mi;
        }
        for(rint i=1;i<=m;++i)
            cout<<ans[i]<<endl;
        return 0;
    }
    View Code

    最开始没设初始区间调了半个小时……

    ps:此题需要卡常,最好使用奇偶性排序法QAQ(18000(TLE63)->12000(AC))

  • 相关阅读:
    数据库被黑后留下的数据
    cron(CronTrigger)表达式用法
    nodeJS常用的定时执行任务的插件
    css实现隐藏滚动条
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
  • 原文地址:https://www.cnblogs.com/xingmi-weiyouni/p/11296071.html
Copyright © 2020-2023  润新知