zoukankan      html  css  js  c++  java
  • 「6月雅礼集训 2017 Day8」gcd

    【题目大意】

    定义times(a, b)表示用辗转相除计算a和b的最大公约数所需步骤。

    那么有:

    1. times(a, b) = times(b, a)

    2. times(a, 0) = 0

    3. times(a, b) = times(b, a mod b) + 1

    对于$1 leq x leq A, 1 leq y leq B$,求times(A, B)的最大值,以及有多少对数取到了最大值。

    多组数据。

    $T leq 3 imes 10^5, 1 leq A,B leq 10^{18}$

    【题解】

    我们打一个1000以内,times(x, y) = 13的表

    我们定义好数对为:$(x,y)$满足不存在$1 leq x' leq x, 1leq y'leq y$,使得times(x', y') > times(x, y)

    那么明显答案肯定是好数,我们发现,答案中的很多数都是由$(x,y)$经过$(x, x+ky)$变换来的。

    那么答案很多,但是$(x,y)$这样的数少,我们定义这样的数为极好数

    严格定义为$x leq fib_{x+2}, y leq fib_{x+2}$且times(x, y) = k的好数。

    我们可以发现并证明(显然),好数只要1步就能变成极好数

    那么我们只要求出极好数,就能推出好数的数量了。

    容易发现极好数很少,只要暴力求即可。

    比如当times = 13的时候,极好数的个数只有13.。。

    (就是把上面所有好数简化过后)

    那么我们就能暴力求啦

    发现times = x的极好数,是根据times = x-1的极好数(x, y)经过变换(y, x+ky)而来。那么我们只要根据极好数的定义求即可。

    复杂度$O(Qlog^2(A))$

    # include <vector>
    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ld;
    
    const int N = 1e5 + 10, M = 2e5 + 10, F = 105;
    const int mod = 1e9 + 7;
    
    inline ll getll() {
        ll x = 0; char ch = getchar();
        while(!isdigit(ch)) ch = getchar();
        while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
        return x;
    }
    
    ll fib[F];
    
    struct pa {
        ll a, b;
        pa() {}
        pa(ll a, ll b) : a(a), b(b) {}
        friend bool operator == (pa a, pa b) {
            return a.a == b.a && a.b == b.b;
        }
        friend bool operator < (pa a, pa b) {
            return a.a < b.a || (a.a == b.a && a.b < b.b);
        }
        friend bool operator > (pa a, pa b) {
            return a.a > b.a || (a.a == b.a && a.b > b.b);
        }
    };
    
    vector<pa> g[M];
    
    int times = 0;
    inline int gcd(ll a, ll b) {
        if(a < b) swap(a, b);
        if(b == 0) return a;
        ++times;
        return gcd(b, a%b);
    }
    
    inline int calc(ll a, ll b) {
        times = 0;
        gcd(a, b);
        return times;
    }
    
    inline void gg(int x) {
        pa t; g[x].clear();
        for (int i=0; i<g[x-1].size(); ++i) {
            t = g[x-1][i];
            ll y = t.b; swap(t.a, t.b);
            for (t.b += y; t.b <= fib[x+2]; t.b += y) 
                if(calc(t.a, t.b) == x) g[x].push_back(t);
        }
        sort(g[x].begin(), g[x].end());
        g[x].erase(unique(g[x].begin(), g[x].end()), g[x].end());
    }
    
    ll A, B, ans, tem;
    inline void sol() {
        A = getll(), B = getll(); 
        if(A > B) swap(A, B);
        ans = 1; tem = 0;
        for (int i=2; i<90; ++i)
            if(fib[i] <= A && fib[i+1] <= B) ans = i;
            else break;    
        printf("%d ", ans);
        if(ans == 1) {
            printf("%d
    ", A % mod * (B % mod) % mod);
            return ;
        } else {
            for (int i=0; i<g[ans-1].size(); ++i) {
                ll a = g[ans-1][i].a, b = g[ans-1][i].b;
                if(b <= A) tem += (B-a)/b, tem %= mod;
                if(b <= B) tem += (A-a)/b, tem %= mod;
            }
        }
        printf("%lld
    ", tem);
    }
    
    int main() {
    //    freopen("gcd.in", "r", stdin);
    //    freopen("gcd.out", "w", stdout);
        fib[0] = 1, fib[1] = 1;
        for (int i=2; i<=90; ++i) fib[i] = fib[i-1] + fib[i-2];
        g[1].push_back(pa(1ll, 2ll)), g[1].push_back(pa(1ll, 3ll));
        for (int i=2; i<=88; ++i) gg(i);
        int T; cin >> T;
        while(T--) sol();
        return 0;
    }
    View Code

    upd: 加了个读入优化跑了rk2 

  • 相关阅读:
    Airtest环境搭建及介绍
    再谈PHP错误与异常处理
    Composer基础
    PHP中this,self,parent的区别
    3种方法轻松处理php开发中emoji表情的问题
    php防注入和XSS攻击通用过滤.
    mysql where in 数组解决小tips
    记录搜索关键字到数据库
    获取用户id的方法
    file_get_contents('php://input') 数据如何转换成数组
  • 原文地址:https://www.cnblogs.com/galaxies/p/20170624_c.html
Copyright © 2011-2022 走看看