Codeforces 979D (STL set)(不用Trie簡單AC)

題面:

傳送門
題目大意:
給定一個空集合,有兩種操作:
一種是往集合中插入一個元素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時間複雜度O(nlog2n)
操作2時間複雜度 O(log2n

代碼:

#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));
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章