zoukankan      html  css  js  c++  java
  • 题解 P5048 【[Ynoi2019模拟赛]Yuno loves sqrt technology III】

    未经允许,禁止转载

    原文链接

    其实本蒟蒻自己想了一会后便有了一些思路,但实现方面还是还是学习了( ext{dllxl})的做法的

    强制在线导致一些离线做法没有办法解决(如莫队),我们考虑分块,预处理出第l到第r个块的众数次数,再把零散块的数一个一个更新答案。

    我们用一个vector存某个值的下标序列,设当前答案是ans,如果当前点在左边,它的后ans+1个元素在r内,则更新答案,右边则反之。

    然而今天我是来教大家卡常的

    关于这个题的提交记录真的卡的我心态爆炸

    好像交了一次题解都没过? 不要封我

    好了,正文开始,首先了解一下这个火车头

    这是个好东西,可以让你(n^2)过百万(雾

    但加了这个好像还是T完了?

    没事,我们还有IO优化

    下面是我了机房大佬的IO优化

    namespace quick {
    #define tp template<typename Type>
        namespace in {
            inline char getc() {
                static char buf[1<<21],*p1=buf,*p2=buf;
                return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
            }
            inline int read(char *s) {
                *s=getc();
                while(isspace(*s)) {*s=getc();if(*s==EOF) return 0;}
                while(!isspace(*s)&&*s!=EOF) {s++;*s=getc();}
                *s=''; return 1;
            }
            tp inline int read(Type &x) {
                x=0;bool k=false;char c=getc();
                while(!isdigit(c)) {k|=(c=='-');c=getc();if(c==EOF) return 0;}
                while(isdigit(c)) {x=(x<<1)+(x<<3)+(c^48);c=getc();}
                x*=(k?-1:1); return 1;
            }
            template <typename Type,typename... Args>
            inline int read(Type &t,Args &...args) {
                int res=0;
                res+=read(t);res+=read(args...);
                return res;
            }
        }
        using in::read;
        namespace out {
            char buf[1<<21];int p1=-1;const int p2=(1<<21)-1;
            inline void flush() {
                fwrite(buf,1,p1+1,stdout);
                p1=-1;
            }
            inline void putc(const char &c) {
                if(p1==p2) flush();
                buf[++p1]=c;
            }
            inline void write(char *s) {
                while(*s!='') putc(*s),s++;
            }
            tp inline void write(Type x) {
                static char buf[30];int p=-1;
                if(x<0) {putc('-');x=-x;}
                if(x==0) putc('0');
                else for(;x;x/=10) buf[++p]=x%10+48;
                for(;p!=-1;p--) putc(buf[p]);
            }
            inline void write(char c) {putc(c);}
            template <typename Type,typename... Args>
            inline void write(Type t,Args ...args) {
                write(t);write(args...);
            }
        }
        using out::write;
        using out::flush;
        tp inline Type max(const Type a,const Type b) {
            if(a<b) return b;
            return a;
        }
        tp inline Type min(const Type a,const Type b) {
            if(a<b) return a;
            return b;
        }
        tp inline void swap(Type &a,Type &b) {
            a^=b^=a^=b;
        }
        tp inline Type abs(const Type a) {
            return a>=0?a:-a;
        }
    #undef tp
    }
    using namespace quick;
    

    直接read,write就ok了

    还有一个很关键很关键的优化(敲黑板

    这是我们在计算第i个块到第j个块的代码,( ext{kmx[][]})就是那个数组,tot是块总数。

        for(int i=1;i<=tot;++i)
        {
            for(int j=i;j<=tot;++j)
            {
                kmx[i][j]=kmx[i][j-1];
                for(int k=kn[j-1]+1;k<=kn[j];++k)
                    kmx[i][j]=max(kmx[i][j],++vis[al[k]]);
            }
            memset(vis,0,sizeof(vis));
        }
    

    但我们可以发现第一个块与最后一个块根本用不上,所以可以改成

        for(int i=2;i<tot;++i)
        {
            for(int j=i;j<tot;++j)
            {
                kmx[i][j]=kmx[i][j-1];
                for(int k=kn[j-1]+1;k<=kn[j];++k)
                    kmx[i][j]=max(kmx[i][j],++vis[al[k]]);
            }
            memset(vis,0,sizeof(vis));
        }
    

    大约可以快0.1s。

    还有要用memset,它绝对比其他方式都快(至少这个题是的)。

    还有max不要用STL,自己手写要快一点。

    还有lxl是真的dio

    完整代码

    inline int max(const int &a,const int &b)
    {
        return a>b?a:b;
    }
    
    int main()
    {
        int t,pre=0;
        read(n,t);
        for(int i=1;i<=n;++i)
            read(a[i]),b[i]=a[i];
        tot=(n-1)/mxn+1;
        for(int i=1;i<=tot;++i)
            kn[i]=kn[i-1]+tot;
        kn[tot]=n;
        // tot=(n-1)/mxn+1;
        // for(int i=1;i<=tot;++i)
        //     kn[i]=kn[i-1]+mxn;
        // kn[tot]=n;
        for(int i=1;i<=tot;++i)
            for(int j=kn[i-1]+1;j<=kn[i];++j)
                bel[j]=i;
        sort(b+1,b+n+1);
        m=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=n;++i)
            al[i]=lower_bound(b+1,b+m+1,a[i])-b;
        for(int i=2;i<tot;++i)
        {
            for(int j=i;j<tot;++j)
            {
                kmx[i][j]=kmx[i][j-1];
                for(int k=kn[j-1]+1;k<=kn[j];++k)
                    kmx[i][j]=max(kmx[i][j],++vis[al[k]]);
            }
            memset(vis,0,sizeof(vis));
        }
        for(int i=1;i<=n;++i)
        {
            pos[al[i]].push_back(i);
            vps[i]=pos[al[i]].size()-1;
        }
        int l,r;
        while(t--)
        {
            read(l,r);
            l^=pre;r^=pre;
            int pl=bel[l],pr=bel[r];
            if(pl==pr)
            {
                int nowans=0;
                for(int i=l;i<=r;++i)
                    nowans=max(nowans,++vis[al[i]]);
                for(int i=l;i<=r;++i)
                    vis[al[i]]=0;
                write(nowans,'
    ');
                pre=nowans;
            }
            else
            {
                int nowans=kmx[pl+1][pr-1];
                for(int i=l;i<=kn[pl];++i)
                {
                    int siz=pos[al[i]].size();
                    while(vps[i]+nowans<siz&&pos[al[i]][vps[i]+nowans]<=r) ++nowans;
                }
                for(int i=kn[pr-1]+1;i<=r;++i)
                    while(vps[i]-nowans>=0&&pos[al[i]][vps[i]-nowans]>=l) ++nowans;
                write(nowans,'
    ');
                pre=nowans;
            }
        }
        return 0;
    }
    

    后记

    首先我的代码不一定在全天候都能过(但你凌晨交是肯定能过),另外如果你认真读了我的代码,你就会发现我用的是固定块长mxn=708,但我在初始化块的时候是这么写的

        tot=(n-1)/mxn+1;
        for(int i=1;i<=tot;++i)
            kn[i]=kn[i-1]+tot;
        kn[tot]=n;
    

    kn是块的右端点,实际上应是

        tot=(n-1)/mxn+1;
        for(int i=1;i<=tot;++i)
            kn[i]=kn[i-1]+mxn;
        kn[tot]=n;
    

    但上面的写法竟然AC了,而且比下面的快,这也算玄学卡常吗

    希望有大佬能解释

    完结撒花!

  • 相关阅读:
    程序的链接
    Graphviz 画图的一些总结
    C表达式中的汇编指令
    epoll(2) 源码分析
    epoll(2) 使用及源码分析的引子
    eventfd(2) 结合 select(2) 源码分析
    poll(2) 源码分析
    select 源码分析
    kfifo
    程序的机器级表示:寻址方式、指令及栈的运行机制
  • 原文地址:https://www.cnblogs.com/gxm123/p/13448658.html
Copyright © 2011-2022 走看看