阿里7.20机考题

1. 给定N和K,求互不相同的正整数x,y,z使得x+y+z=N,且gcd(x,y)=gcd(x,z)=gcd(y,z)=K。
条件:1 ≤N, K≤ 1e18
思路:等式两边除K,得到x'+y'+z'=N'=N/K,且x',y',z'两两互素。
当N'为偶数,直接构造x'=1, y'=N'/2, z' = y'-1满足条件。
当N'为奇数,另x'=1,则y'+z'=N'-1。由于N'-1为偶数且y'和z'互素,必然有y',z'都为奇数。令y'=3,5,..., N' / 2逐个搜索即可。

在这里讨论一下N'为奇数时搜索的复杂度。
考虑奇素数pi,如果y'=pi不满足条件,那么必然有z'与pi不互素,也就是pi整除z',从而pi整除pi+z'=N'-1
考虑y'为前15个奇素数p1,...,p15。如果均不满足条件,那么他们的乘积p1p2...p15也整除N'-1。考虑到前15个奇素数的积已经超过了1e18,矛盾。
因此我们搜索到第15个奇素数(53)的时候一定能找到一对满足条件的y'和z'。因此搜索复杂度为O(p15)=O(1)。


using ll = long long;

for (int kase = 1; kase <= T; kase++) {
    cin >> n >> k;
    if (n % k != 0) cout << -1 << endl;
    else {
        n /= k;
        if (n < 6) cout << -1 << endl;
        else if (n % 2 == 0) {
            ll j = (n - 1) / 2;
            cout << k << " " << k * j << " " << k * (j + 1) << endl;
        }
        else {
            bool found = false;
            for (ll p = 3; p < n - 1 - p && !found; p += 2) {
                ll q = n - 1 - p;
                if (__gcd(p, q) == 1) {
                    cout << k << " " << k * p << " " << k * q << endl;
                    found = true;
                }
            }
            if (!found) cout << -1 << endl;
        }
    }
}

2. 求区间[l, r]内的幸运数。幸运数定义为,将相邻数位差的绝对值拼成下一个数,重复该操作直到只剩1位。剩下7的是幸运数。例如,219->18->7或者118->7
条件:1 ≤ l ≤ r ≤ 1e9
思路:根据1,...,k-1位的幸运数,给定首位数字,可以搜索得到k位的幸运数。将所有幸运数排序后进行二分查找。

因为每一个更长的幸运数都可以通过题目里规定的操作变成一个更短的幸运数,反过来讲,给定每个幸运数的首位 for i in {1, ..., 9},它都可以通过一个比他短的幸运数计算得到。
比如给定首位是1,可以算 7->18。根据18,给定首位是2,又可以算出18->219。
不断这样以小算大,可以算出所有幸运数。
注意k位的幸运数不仅可以通过k-1的幸运数计算得到,还可以通过更短的幸运数在前方补0到k-1位后计算得到。例如:7补成007,可以计算得到1118,2229,7770,8881,9992。

void dfs(vector<pair<int, vector<int>>> mem[], vector<int>& ref, int digits, int cur, int idx) {
    if (idx == digits) {
        vector<int> dcur;
        for (int c = cur; c; c /= 10) dcur.push_back(c % 10);
        reverse(dcur.begin(), dcur.end());
        mem[digits].push_back({cur, dcur});
    }
    else if (idx == 0) {
        for (int i = 1; i <= 9; i++)
            dfs(mem, ref, digits, cur * 10 + i, idx + 1);
    }
    else {
        int pos = idx - (digits - (int)ref.size());
        int diff = pos >= 0 ? ref[pos] : 0;
        int last = cur % 10;
        if (diff == 0)
            dfs(mem, ref, digits, cur * 10 + last, idx + 1);
        else {
            for (int sign : {-1, 1}) {
                int val = last + diff * sign;
                if (val < 0 || val > 9) continue;
                else dfs(mem, ref, digits, cur * 10 + val, idx + 1);
            }
        }
    }
}

int main() {
    const int maxdigits = 9;
    vector<pair<int, vector<int>>> luckies[maxdigits + 1];

    luckies[1].push_back({7, {7}});
    for (int digits = 2; digits <= maxdigits; digits++)
        for (int i = 1; i <= digits - 1; i++)
            for (auto& ref : luckies[i])
                dfs(luckies, ref.second, digits, 0, 0);

    vector<int> lucky_nums;
    for (int digits = 1; digits <= maxdigits; digits++)
        for (auto& v : luckies[digits])
            lucky_nums.push_back(v.first);
    sort(lucky_nums.begin(), lucky_nums.end());

    int T, l ,r;
    cin >> T;
    for (int ks = 1; ks <= T; ks++) {
        cin >> l >> r;
        auto pr = upper_bound(lucky_nums.begin(), lucky_nums.end(), r);
        auto pl = upper_bound(lucky_nums.begin(), lucky_nums.end(), l - 1);
        cout << pr - pl << endl;
    }
    return 0;
}





#笔试题目#
全部评论
看到学校,我放心了
5 回复 分享
发布于 2020-07-20 20:28
妈妈问我为什么跪着看代码
2 回复 分享
发布于 2020-07-23 15:18
一道题,没写出来😂
点赞 回复 分享
发布于 2020-07-21 13:28
😭😭没做出来
1 回复 分享
发布于 2020-07-20 20:12
请问大佬,您第二题的思路意思是求出所有的幸运数是吗,从二位到三位再到四位这么算是吗?那我有两个问题可以帮忙解答一下吗? 1.每次把当前位数的算完需要进位求下一个维度的幸运数时,是不是需要把之前所有的都加零补全,然后以1到9开头都判断一边是否符合? 2.求完之后是否还要排序?因为比如7,18,29,70,81,92之后算三位数的是118,108,188,198,229,219,299,770,780,790,700,881,891,811,801,992,980,922,910,912,902其实是杂乱无序的,而且和之前的位置没有很强的关联性,所以是算完之后排序还是应该一边排一边算
点赞 回复 分享
发布于 2020-07-30 11:58
算法岗和开发岗笔试不一样吧,感觉太偏数学啦😂
点赞 回复 分享
发布于 2020-07-28 09:56
弱弱的问一句,笔试可以用matlab语言编程吗
点赞 回复 分享
发布于 2020-07-22 16:08
第二题代码风格好棒,一看就是acmer,一看学校。。。
点赞 回复 分享
发布于 2020-07-21 19:08
我连题目都看错了吗?我咋记得是两两最大公约数是K。😥
点赞 回复 分享
发布于 2020-07-21 16:02
对了,楼主 我是弱鸡,第二题可以讲解一些思路吗
点赞 回复 分享
发布于 2020-07-21 12:17
大佬。我没有参加考试,我觉得这样写我想不到, 你看我这样写的对吗 ll N,K; ll gcd(ll a, ll b) {     if(b == 0) return a;     else return gcd(b,a%b); } int main() {     cin >> N >> K;     if(N % K)      {         cout << -1 <<endl;         return 0;     }     else     {         N /= K;         // a b c互质         if(n < 6) cout << -1 <<endl;         for(ll i = 1; i < N - 2; i++)         {             ll l = i + 1;             ll r = N - 1;             while(l < r)             {                 if(i + l + r == N)                 {                     if(gcd(i,l) == gcd(i,r) == gcd(l,r) == 1)                     {                         cout << i  << " " << l << " " << r << endl;                         return 0;                     }                                  l++;                 }                 else if(i + l + r > N)                 {                     r--;                 }                 else                  {                     l++;                 }             }         }         cout << -1 << endl;
点赞 回复 分享
发布于 2020-07-21 12:09
吸一波
点赞 回复 分享
发布于 2020-07-21 12:00
可以,清华大佬,。我裂开了。
点赞 回复 分享
发布于 2020-07-21 11:50
清华大佬
点赞 回复 分享
发布于 2020-07-21 00:08
我直接裂开
点赞 回复 分享
发布于 2020-07-21 00:07
大佬的代码真工整啊 就像高中学霸的板书
点赞 回复 分享
发布于 2020-07-20 23:55
路过,没有看过完整问题,所以不是很理解,问一下 N=30,k=2,abc=6,10,14这种情况不行吗
点赞 回复 分享
发布于 2020-07-20 23:00
偶数 1,x,x+1 奇数 1 x n-x-1  还是挺好构造的吧
点赞 回复 分享
发布于 2020-07-20 22:21
第二题可暴力打表吧,感觉lucky数不多。
点赞 回复 分享
发布于 2020-07-20 21:53
tql,只写出第一题😂
点赞 回复 分享
发布于 2020-07-20 21:40

相关推荐

不愿透露姓名的神秘牛友
06-10 15:24
高考前一晚在OPPO手机上设置了早上5:30的闹钟,然而闹钟并未按时响起。直到妈妈做好早餐后,在6:27打开手机才发现闹钟未触发,“气得早上饭都没吃”。资本家你赢了
永不遗忘:我来解释一下 :Oppo 手机晚上两点会自动进行系统更新,这个系统更新会重置掉所有设置好的闹钟,而且他也不会告诉你,而且只有 Oppo 会这样,华为苹果小米三星都不会
点赞 评论 收藏
分享
05-25 10:45
门头沟学院 Java
Frank_zhang:没实习一个项目肯定不够,可以再做一个轮子,技术栈再补一个mq,微服务,整体再换个简历模板,暑期尽量再找一个日常实习
点赞 评论 收藏
分享
那一天的Java_Java起来:他本来公司就是做这个的,不就是正常的游戏客户端和服务器开发,软硬件联动,有啥恶心不恶心的,提前告诉你就是怕你接受不了,接受不了就没必要再往后走流程浪费时间,虽然这公司是一坨。
点赞 评论 收藏
分享
评论
33
109
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务