【BZOJ4810】[YNOI2017] 由乃的玉米田(莫隊+bitset)
阿新 • • 發佈:2018-11-22
大致題意: 給你一段序列,每次詢問一段區間內是否存在兩個數的差或和或積為\(x\)。
莫隊演算法
看到區間詢問+可以離線,首先想到了莫隊啊。
但是,在較短的時間內更新資訊依然比較難以實現。
於是,我們就要考慮用\(bitset\)了。
關於\(bitset\)
這應該是我第一次使用\(bitset\)吧,所以簡單介紹一下它的使用方式。
其作用就相當於儲存一個特別大的二進位制數。可以把它看成一個\(bool\)陣列來使用。
它的好處就在於,它可以直接進行\(\&,|,\text{^},<<,>>\)等各種位運算操作。
它有一個比較常用的函式:\(any()\)
另外,還有一個函式\(count()\)是統計有幾個\(1\)。
實際上,瞭解了這些,我們就可以用\(bitset\)來做這題了。
大致思路
考慮開兩個\(bitset\):\(s1\)和\(s2\),其中\(s1_i\)表示值為\(i\)的元素是否存在,\(s2_i\)表示值為\(N-i\)的元素是否存在。
這樣一來,似乎就不難處理差值的操作了,答案就是\((s1\&(s1<<x)).any()\),這還是比較好理解的,即判斷是否有一個數和比它大\(x\)的數同時存在。
同理可得,和的操作答案就是\((s1\&(s2>>N-x)).any()\)
對於積的操作就略麻煩了一點,需要列舉因數\(j\),然後判斷\(j\)和\(\frac xj\)是否同時存在即可,這個操作是\(O(\sqrt x)\)的。
具體實現可見程式碼。
程式碼
#include<bits/stdc++.h> #define N 100000 #define abs(x) ((x)<0?-(x):(x)) using namespace std; int n,query_tot,a[N+5]; class Class_FIO { private: #define Fsize 100000 #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++) char ch,*A,*B,Fin[Fsize]; public: Class_FIO() {A=B=Fin;} inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));} }F; class Class_CaptainMotao//莫隊 { private: int block_size,ans[N+5],cnt[N+5];bitset<N+5> s1,s2; inline void Add(int x) {if(!cnt[a[x]]++) s1[a[x]]=s2[N-a[x]]=1;}//新加一個元素 inline void Del(int x) {if(!--cnt[a[x]]) s1[a[x]]=s2[N-a[x]]=0;}//刪除一個元素 public: struct Query { int l,r,val,op,pos,bl; inline friend bool operator < (Query x,Query y) {return x.bl^y.bl?x.bl<y.bl:(x.bl&1?x.r<y.r:x.r>y.r);} }q[N+5]; inline void Solve() { int i,j,L=1,R=0; for(block_size=sqrt(n),i=1;i<=query_tot;++i) F.read(q[i].op),F.read(q[i].l),F.read(q[i].r),F.read(q[i].val),q[q[i].pos=i].bl=(q[i].l-1)/block_size+1;//讀入 for(L=1,R=0,sort(q+1,q+query_tot+1),i=1;i<=query_tot;++i) { while(R<q[i].r) Add(++R);while(L>q[i].l) Add(--L);while(R>q[i].r) Del(R--);while(L<q[i].l) Del(L++); switch(q[i].op) { case 1:ans[q[i].pos]=(s1&(s1<<q[i].val)).any();break;//對於差的操作 case 2:ans[q[i].pos]=(s1&(s2>>N-q[i].val)).any();break;//對於和的操作 case 3:for(j=1;1LL*j*j<=q[i].val&&!ans[q[i].pos];++j) !(q[i].val%j)&&s1[j]&&s1[q[i].val/j]&&(ans[q[i].pos]=1);break;//對於積的操作 } } for(i=1;i<=query_tot;++i) puts(ans[i]?"yuno":"yumi");//輸出答案 } }C; int main() { register int i; for(F.read(n),F.read(query_tot),i=1;i<=n;++i) F.read(a[i]); return C.Solve(),0; }