Codeforces 979D Kuro and GCD and XOR and SUM 题解(STL,瞎搞)

原题链接:
CF:点我QωQ
洛谷:点我QωQ

题意简述

初始有一个空的集合,和 Q Q Q个操作。对于每个操作,有两种类型,分别用如下的两种形式表示:
1   u 1\ u 1 u:加入 u u u到集合
2   x   k   s 2\ x\ k\ s 2 x k s:求一个最大的 v v v,使得:

  1. v + x &lt; s v+x&lt;s v+x<s
  2. k ∣ g c d ( v , x ) k∣gcd(v,x) kgcd(v,x)
  3. x ⊕ v x \oplus v xv最大(其中 ⊕ \oplus 表示按位异或,对应 C + + C++ C++中的^运算符)
    如果找不到满足条件的 v v v,输出 − 1 −1 1

数据

输入

第一行是一个正整数 Q Q Q
接下来 Q Q Q行,每行一个操作,如题意简述中所描述。

输出

对于每个 2 2 2操作,输出答案,或者 − 1 -1 1

思路

首先我们要明确一个问题:如果没有任何限制,只是求异或和最大,怎么求?

这是一个非常经典的问题:最大异或对。我们采用 01 − T R I E 01-TRIE 01TRIE按位跑一遍,每次贪心选择,求出答案。珂是 T R I E TRIE TRIE虽然好理解,但是不好写,如果一不小心写挂了,就要浪费不少时间。而且, C F CF CF上也很提倡一题多解,支持各种玄学方法,时间很足,空间也足,只要不是暴力,都过得去。

所以我们想到用 S T L STL STL s e t set set乱搞,看能不能过这个题。准确来讲,我的这个算法是 O ( n 2 ) O(n^2) O(n2)的,但是要知道几点,

  1. C F CF CF机快
  2. 数据水
  3. 这题时限 2 s 2s 2s

综上, O ( n 2 ) O(n^2) O(n2)能过。所以,我们用 s e t set set开个数组, s e t &lt; i n t &gt; r e c [ N ] set&lt;int&gt;rec[N] set<int>rec[N] r e c [ i ] rec[i] rec[i]表示给定的数中是 i i i的倍数的那些。然后,我们在询问的时候,在 r e c [ i ] rec[i] rec[i]中找到最后一个 &lt; s − x &lt;s-x <sx的位置(即 u p p e r _ b o u n d ( s − x ) − 1 upper\_bound(s-x)-1 upper_bound(sx)1),暴力枚举,看看哪个最大。当然记得判边界。事实证明这非常快,能 A C AC AC

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define N 100100
    set<int> rec[N];
    set<int>::iterator It;//一个临时迭代器
    void Insert(int x)
    {
        for(int i=1;i*i<=x;++i)
        {
            if (x%i==0)
            {
                rec[i].insert(x);
                rec[x/i].insert(x);
            }
        }//每次要枚举x的因数,都去存一下
    }
    int calc(int x,int k,int s)
    {
        if (x%k!=0) return -1;
        if (rec[k].size()==0) return -1;//剪枝(没啥用)
        It=rec[k].upper_bound(s-x);
        if (It==rec[k].begin()) return -1;
        //如果It==rec[k].begin(),那么-1会很危险,特判
        --It;

        int ans=-1,sum=-1;
        for(;It!=rec[k].begin();--It)//暴力
        {
            int tmp=*It;
            if (sum>x+tmp) break;
            if ((tmp^x)>sum)
            {
                sum=tmp^x;
                ans=tmp;//更新答案
            }
        }
        if ((x^(*rec[k].begin()))>sum) ans=*rec[k].begin();
        //begin特判
        return ans;
    }
    void Query()
    {
        int q;scanf("%d",&q);
        while(q--)
        {
            int o;scanf("%d",&o);
            if (o==1)
            {
                int x;scanf("%d",&x);
                Insert(x);
            }
            else if (o==2)
            {
                int x,k,s;
                scanf("%d%d%d",&x,&k,&s);
                printf("%d\n",calc(x,k,s));
            }
        }
    }
    void Main()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        Query();
    }
};
main()
{
    Flandle_Scarlet::Main();
    return 0;
}

回到总题解界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值