【題解】Luogu P5072 [Ynoi2015]盼君勿忘
阿新 • • 發佈:2018-12-08
眾所周知lxl是個毒瘤,Ynoi道道都是神仙題,題面好評
原題傳送門
一看這題沒有修改操作就知道這是莫隊題
我部落格裡對莫隊的簡單介紹
既然是莫隊,我們就要考慮每多一個數或少一個數對答案的貢獻是什麼
假設一個數x在區間[l,r]之間出現了y次,珂以很容易的求出該區間的長度length=r-l+1,那麼包含x的區間有\(2^{length-y}*(2^y-1)\),那麼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} \rfloor *bl +n \mod bl}\),其中\(bl=\lfloor \sqrt n \rfloor\)
每次只需要預處理\(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)\)
完整程式碼
// luogu-judger-enable-o2 #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; }