Luogu P5072 [Ynoi2015]盼君勿忘
阿新 • • 發佈:2020-09-19
題意
給定一個長度為 \(n\) 的序列 \(a\) 和 \(m\) 次詢問,第 \(i\) 次詢問需要求出 \([l_i,r_i]\) 內所有子序列去重之後的和,對 \(p_i\) 取模。
\(\texttt{Data Range:}1\leq n,m,a_i\leq 10^5,1\leq p_i\leq 10^9\)
題解
人生第一道 Ynoi,寫篇題解祭之。
我們與其考慮某個子序列包含了哪些值,還不如看某個值能貢獻到多少個子序列。
然而正著做不好做,因為一個子序列中某個值可能出現多次,所以考慮反過來看一個值不能貢獻到多少個子序列,再用總的減去這個子序列就好了。
考慮某一個 \([l,r]\)
同時,注意到多個出現次數相同的數可以一起加起來貢獻。所以我們可以考慮設 \(b_k\) 為當前的區間內出現了 \(k\) 次的所有數的和,那麼我們可以得到答案為
\[\sum\limits_{k}b_k\left(2^{r-l+1}-2^{r-l+1-k}\right) \]
注意到 \(b_k\) 可以使用莫隊來維護,所以我們就得到了一個 \(O(nm\log n)\) 的演算法,但是無法通過。
考慮統計答案的時候我們會列舉很多等於 \(0\)
注意到連結串列中記錄的 \(k\) 是 \(O(\sqrt{n})\) 的,所以時間複雜度就變為 \(O(m\sqrt{n}\log n)\),還是會 TLE。
注意到這個 \(\log\) 甚至都可以搞掉,考慮分塊打表,對於每個詢問都預處理一次,因為一次預處理是 \(O(\sqrt{n})\) 的,所以複雜度為 \(O(m\sqrt{n})\),可過。
程式碼
#include<bits/stdc++.h> #pragma GCC optimize("Ofast,unroll-loops") using namespace std; typedef int ll; typedef long long int li; const ll MAXN=2e5+51; struct Query{ ll l,r,p,id; inline bool operator <(const Query &rhs)const; }; Query qry[MAXN]; ll n,qcnt,l,r,p,blockSize,ptrl,ptrr,len,hd; li rres; ll x[MAXN],res[MAXN],cntl[MAXN],sum[MAXN],prv[MAXN],nxt[MAXN]; ll blk[MAXN],pw[MAXN]; inline ll read() { register ll num=0,neg=1; register char ch=getchar(); while(!isdigit(ch)&&ch!='-') { ch=getchar(); } if(ch=='-') { neg=-1; ch=getchar(); } while(isdigit(ch)) { num=(num<<3)+(num<<1)+(ch-'0'); ch=getchar(); } return num*neg; } inline bool Query::operator <(const Query &rhs)const { if(l/blockSize==rhs.l/blockSize) { return l/blockSize==1?r<rhs.r:r>rhs.r; } return l<rhs.l; } inline void insert(ll x) { prv[x]=0,nxt[x]=hd,prv[hd]=x,hd=x; } inline void erase(ll x) { if(x==hd) { return (void)(hd=nxt[x],prv[nxt[x]]=prv[x]=nxt[x]=0); } nxt[prv[x]]=nxt[x],prv[nxt[x]]=prv[x],prv[x]=nxt[x]=0; } inline void add(ll pos) { if(!(cntl[x[pos]]++)) { sum[1]+=x[pos]; } else { sum[cntl[x[pos]]-1]-=x[pos],sum[cntl[x[pos]]]+=x[pos]; } if(sum[cntl[x[pos]]]==x[pos]) { insert(cntl[x[pos]]); } if(!sum[cntl[x[pos]]-1]) { erase(cntl[x[pos]]-1); } } inline void del(ll pos) { if(!(--cntl[x[pos]])) { sum[1]-=x[pos]; } else { sum[cntl[x[pos]]+1]-=x[pos],sum[cntl[x[pos]]]+=x[pos]; } if(sum[cntl[x[pos]]]==x[pos]) { insert(cntl[x[pos]]); } if(!sum[cntl[x[pos]]+1]) { erase(cntl[x[pos]]+1); } } inline void setup(ll md) { pw[0]=blk[0]=1; for(register int i=1;i<=511;i++) { pw[i]=(pw[i-1]+pw[i-1])%md; } blk[1]=(pw[511]+pw[511])%md; for(register int i=1;i<=512;i++) { blk[i]=(li)blk[i-1]*blk[1]%md; } } inline ll query(ll x,ll md) { return (li)blk[x>>9]*pw[x&511]%md; } int main() { blockSize=sqrt(n=read()),qcnt=read(); for(register int i=1;i<=n;i++) { x[i]=read(); } for(register int i=1;i<=qcnt;i++) { l=read(),r=read(),p=read(),qry[i]=(Query){l,r,p,i}; } sort(qry+1,qry+qcnt+1),ptrl=1; for(register int i=1;i<=qcnt;i++) { while(ptrr<qry[i].r) { add(++ptrr); } while(ptrr>qry[i].r) { del(ptrr--); } while(ptrl<qry[i].l) { del(ptrl++); } while(ptrl>qry[i].l) { add(--ptrl); } setup(p=qry[i].p),len=qry[i].r-qry[i].l+1,rres=0; for(register int j=hd;j;j=nxt[j]) { rres+=(li)sum[j]*(query(len,p)-query(len-j,p)); } res[qry[i].id]=(rres%p+p)%p; } for(register int i=1;i<=qcnt;i++) { printf("%d\n",res[i]); } }