Luogu

分析

考虑每个数的贡献。假设 $x$ 在 $[l,r]$ 中出现了 $c$ 次,则有 $2^{r-l+1}-2^{r-l+1-c}$ 个子序列包含 $x$,所以其贡献为 $x(2^{r-l+1}-2^{r-l+1-c})$。

考虑把出现次数相同的放到一起,即求出每种出现次数对应的数的和。显然可以直接莫队。

注意到出现次数最多只有 $\mathcal{O}(\sqrt{n})$ 种,所以我们可以直接暴力枚举出现次数算答案。

然而我们并不能 $\mathcal{O}(n)$ 枚举出现次数,用 std::unordered_set 或者双向链表维护所有 $\mathcal{O}(\sqrt{n})$ 种出现次数即可。

还有一个问题是如何快速求出 $2$ 的幂。可以对每个询问处理出 $2^0,2^1,\cdots,2^{317}$ 和 $2^{2\times 317},2^{3\times 317},\cdots,2^{317\times 317}$,即可做到 $\mathcal{O}(1)$ 快速幂。

有些卡常。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
#pragma GCC optimize("Ofast,unroll-loops,fast-math")
using namespace std;
typedef long long ll;

namespace io {
    const int SIZE=1<<21|1;
    char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55]; int f,qr;
    #define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
    void flush() { fwrite(obuf,1,oS-obuf,stdout); oS=obuf; }
    void putc(char x) { *oS++=x; if (oS==oT) flush(); }
    void read(int& x) {
        for (f=1,c=gc();c<'0'||c>'9';c=gc()) if (c=='-') f=-1;
        for (x=0;c<='9'&&c>='0';c=gc()) x=x*10+(c&15); x*=f;
    }
    void write(int x) {
        if (!x) putc ('0'); if (x<0) putc('-'),x=-x;
        while (x) qu[++qr]=x%10+'0',x/=10;
        while (qr) putc(qu[qr--]);
    }
    struct Flusher_ { ~Flusher_() { flush(); } } io_flusher_;
}
using io::putc;
using io::read;
using io::write;

const int N=100000+10;

int n,m,B,a[N],ans[N];
struct query { int l,r,mod,id; } q[N];
bool operator <(query a,query b) {
    if (a.l/B!=b.l/B) return a.l<b.l;
    else return (a.l/B)&1?a.r<b.r:a.r>b.r;
}

namespace L {
    int pre[N],nxt[N],lst=0;
    void insert(int x) {
        nxt[lst]=x,pre[x]=lst,lst=x;
    }
    void erase(int x) {
        if (x==lst) lst=pre[x];
        else nxt[pre[x]]=nxt[x],pre[nxt[x]]=pre[x];
        pre[x]=nxt[x]=0;
    }
}

int cnt[N],sum[N];
void add(int w) {
    sum[cnt[w]]-=w;
    if (!sum[cnt[w]]) L::erase(cnt[w]);
    ++cnt[w];
    if (!sum[cnt[w]]) L::insert(cnt[w]);
    sum[cnt[w]]+=w;
}
void del(int w) {
    sum[cnt[w]]-=w;
    if (!sum[cnt[w]]) L::erase(cnt[w]);
    --cnt[w];
    if (!sum[cnt[w]]) L::insert(cnt[w]);
    sum[cnt[w]]+=w;
}

int pw1[400],pw2[400];
int qpow(int x,int mod) { return 1ll*pw1[x%317]*pw2[x/317]%mod; }

int main() {
    read(n),read(m); B=sqrt(n);
    for (int i=1;i<=n;++i) read(a[i]);
    for (int i=1;i<=m;++i) read(q[i].l),read(q[i].r),read(q[i].mod),q[i].id=i;
    sort(q+1,q+m+1);
    for (int i=1,l=1,r=0;i<=m;++i) {
        while (l>q[i].l) --l,add(a[l]);
        while (r<q[i].r) ++r,add(a[r]);
        while (l<q[i].l) del(a[l]),++l;
        while (r>q[i].r) del(a[r]),--r;
        int mod=q[i].mod;
        for (int j=pw1[0]=1;j<=317;++j) pw1[j]=1ll*pw1[j-1]*2%mod;
        for (int j=pw2[0]=1;j<=317;++j) pw2[j]=1ll*pw2[j-1]*pw1[317]%mod;
        for (int t=L::nxt[0];t;t=L::nxt[t]) {
            int w=1ll*sum[t]*(qpow(r-l+1,mod)+mod-qpow(r-l+1-t,mod))%mod;
            ans[q[i].id]=(ans[q[i].id]+w)%mod;  
        }
    }
    for (int i=1;i<=m;++i) write(ans[i]),putc('\n');
    return 0;
}
最后修改:2020 年 06 月 17 日 11 : 12 AM