題面:
傳送門
題目大意:
給定一個空集合,有兩種操作:
一種是往集合中插入一個元素x,一種是給三個數x,k,s,問集合中是否存在v,使得gcd(x,v)%k==0,且x+v<=s若存在多個滿足條件,則輸出使得v⊕x最大的v。
分析:
首先,gcd(x,v)%k==0,由數論知識得該條件等價於x%k==0&&v%k==0
那麼,我們怎麼快速求出能整除k的v呢
對操作1輸入的數x的每個因數,我們建立一個集合
s[i]存儲能被i整除的所有x
且由於c++ STL的set的特性,集合內元素從小到大排列,我們可以快速求出x+v<=s的所有v,再從這些值中選出v⊕x最大的v即可
易錯細節
1.在set中查找時我們要記得判斷集合是否爲空
2.注意upper_bound的返回值
3.集合中只有第一個數滿足條件時的特判
因爲我們是這樣倒序遍歷集合的 for(;it!=s[k].begin();it--)
所以當集合中只有第一個數滿足條件時,it=s[k].begin(),會直接跳出循環
在循環結尾做一下特判就可以了
時間複雜度分析:
操作1時間複雜度
操作2時間複雜度
代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<cmath>
#define maxn 100005
using namespace std;
set<int>s[maxn];
int n;
void div(int x){//分解因數,並將x插入每個因數對應的集合
int sq=(int)sqrt(x);
for(int i=1;i<=sq;i++){
if(x%i==0){
s[i].insert(x);
s[x/i].insert(x);
}
}
}
int get_ans(int x,int k,int maxs){
if(x%k!=0) return -1;
set<int>::iterator it;
if(s[k].empty()) return -1;//集合爲空的特判
it=s[k].upper_bound(maxs-x);//查找x+v<=s的最大v (準確的說,是v的下標+1,因爲upper_bound的返回值)
if(it==s[k].begin()) return -1;
it--;//由上知要-1
int ans=-1,sum=-1;
for(;it!=s[k].begin();it--){//從大到小找v⊕x最大的v
int v=*it;
if(sum>x+v) break;//因爲v⊕x<=v+x
if(sum<(x^v)){
ans=v;
sum=x^v;
}
}
if(sum<(x^*it)) ans=*it;//只有第一個數滿足條件時的特判
return ans;
}
int main(){
int cmd,x,k,s;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&cmd);
if(cmd==1){
scanf("%d",&x);
div(x);
}else{
scanf("%d %d %d",&x,&k,&s);
printf("%d\n",get_ans(x,k,s));
}
}
}