zoukankan      html  css  js  c++  java
  • 【题解】Luogu P5072 [Ynoi2015]盼君勿忘

    众所周知lxl是个毒瘤,Ynoi道道都是神仙题,题面好评

    原题传送门

    一看这题没有修改操作就知道这是莫队题

    我博客里对莫队的简单介绍

    既然是莫队,我们就要考虑每多一个数或少一个数对答案的贡献是什么

    假设一个数x在区间[l,r]之间出现了y次,珂以很容易的求出该区间的长度length=r-l+1,那么包含x的区间有(2^{length-y}*(2^y-1))(2^{length-y})表示除了这x个相同的数,其他的数取与不取的情况数,(2^y-1)表示这x个数取与不取的情况数减掉一个不取的情况,那么x对答案的贡献为(x*2^{length-y}*(2^y-1))(x*2^{length}-x*2^{length-y})

    从上式得知,x和y是相独立的,我们只需要统计出现次数为y的数字之和就行了

    不同的y值最多有(sqrt n)种,用一个线性列表维护这(sqrt n)个的出现次数,维护每种出现次数的数字个数和总和,就能把查询的内容简化为一个长度为(sqrt n)的多项式

    由于每次的模数p不同,所以不能预处理2的幂,为了使求和时速度更快(毒瘤),我们把(2^n)变成(2^{lfloor frac{n}{bl} floor *bl +n mod bl}),其中(bl=lfloor sqrt n floor)

    每次只需要预处理(2^0,2^1,...,2^{bl})(2^{bl},2^{2*bl},...,2^{bl*bl})就珂以O(1)求出二的次幂(由于取模过慢,所以我程序里用了加减乘除代替取模)

    转移是(O(N sqrt N))

    查询也是(O(N sqrt N))

    所以总复杂度为(O(N sqrt N))

    完整代码

    #include <bits/stdc++.h>
    #define N 100005
    #define ll long long
    #define getchar nc
    using namespace std;
    inline char nc(){
        static char buf[100000],*p1=buf,*p2=buf; 
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; 
    }
    inline int read()
    {
        register int x=0,f=1;register char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return x*f;
    }
    inline void write(register int x)
    {
        if(!x)putchar('0');if(x<0)x=-x,putchar('-');
        static int sta[20];register int tot=0;
        while(x)sta[tot++]=x%10,x/=10;
        while(tot)putchar(sta[--tot]+48);
    }
    struct query{
        int l,r,p,id,bl;
    }q[N];
    int n,m,blocksize,a[N],ans[N];
    inline bool cmp(register query a,register query b)
    {
        return a.bl!=b.bl?a.l<b.l:((a.bl&1)?a.r<b.r:a.r>b.r);
    }
    int pre[N],nxt[N],head=0,vis[N],cnt[N];
    ll sum[N];
    inline void add(register int x)
    {
        nxt[x]=head,pre[head]=x;
        head=x,pre[x]=0;
    }
    inline void del(register int x)
    {
        if(x==head)
            head=nxt[x];
        else
            nxt[pre[x]]=nxt[x],pre[nxt[x]]=pre[x];
    }
    inline void change(register int x,register int type)
    {
        if(cnt[a[x]])
        {
            sum[cnt[a[x]]]-=a[x];
            if(--vis[cnt[a[x]]]==0)
                del(cnt[a[x]]);
        }
        cnt[a[x]]+=type;
        if(cnt[a[x]])
        {
            sum[cnt[a[x]]]+=a[x];
            if(++vis[cnt[a[x]]]==1)
                add(cnt[a[x]]);
        }
    }
    int p,f[1000],g[1000];
    inline int add(register int x,register int y)
    {
        x+=y;
        if(x>=p)
            x-=p;
        return x;
    }
    inline int mul(register int x,register int y)
    {
        return 1ll*x*y-1ll*x*y/p*p;
    }
    inline void init(register int n)
    {
        f[0]=g[0]=1;
        for(register int i=1;i<=blocksize;++i)
            f[i]=add(f[i-1],f[i-1]);
        for(register int i=1;i<=n/blocksize;++i)
            g[i]=mul(g[i-1],f[blocksize]);
    }
    inline int Pow(register int n)
    {
        return mul(g[n/blocksize],f[n%blocksize]);
    }
    inline int query(register int l,register int r,register int mod)
    {
        p=mod;
        init(r-l+1);
        int ans=0;
        for(register int i=head;i;i=nxt[i])
            ans=add(ans,mul(sum[i]%p,add(Pow(r-l+1),p-Pow(r-l+1-i))));
        return ans;
    }
    int main()
    {
        n=read(),m=read();
        blocksize=(int)sqrt(n);
        for(register int i=1;i<=n;++i)
            a[i]=read();
        for(register int i=1;i<=m;++i)
            q[i].l=read(),q[i].r=read(),q[i].p=read(),q[i].id=i,q[i].bl=(q[i].l-1)/blocksize+1;
        sort(q+1,q+1+m,cmp);
        int l=1,r=0;
        for(register int i=1;i<=m;++i)
        {
            while(l>q[i].l)
                change(--l,1);
            while(r<q[i].r)
                change(++r,1);
            while(l<q[i].l)
                change(l++,-1);
            while(r>q[i].r)
                change(r--,-1);
            ans[q[i].id]=query(q[i].l,q[i].r,q[i].p);
        }
        for(register int i=1;i<=m;++i)
            write(ans[i]),puts("");
        return 0;
    }
    
  • 相关阅读:
    对话框隐藏的简单实现(转)
    CoInitializeSecurity 学习(转)
    VC++实现CD/DVD刻录(转)
    网络连接的保活机制(心跳机制转)
    tcp架构
    木马的隐藏及其启动方式 (转)
    vs2005 控件bug
    vs2005 虚拟调试配置(转)
    sprintf用法(转)
    要掌握真正的免杀必须懂汇编【汇编语言超浓缩教程】(转)
  • 原文地址:https://www.cnblogs.com/yzhang-rp-inf/p/10089265.html
Copyright © 2011-2022 走看看