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
",get_ans(x,k,s));
        }
    }
}
原文地址:https://www.cnblogs.com/birchtree/p/9845837.html