数论习题集

1.F-Ginger的数_牛客小白月赛63 (nowcoder.com) (exgcd)

        (1)题目大意

         (2)解题思路

                考虑到只能用[1,m-1]中间的数,因此我们预处理出[1,m-1]中1位的有多少个数字,2位的有多少个数字,一直到m的最高位,然后我们用双重循环枚举需要用到第i位的数字到第j位的数字,中间的全部选上,若中间全部选上的个数已经大于n,则无解,最后第i位和第j位需要的数量位i*x + j * y = gcd(i,j),若剩下的数量不能整除gcd(i,j)说明此方程无解,最后我们考虑把y调的更大,则答案越小,因此我们调节y,若y+1个,则数量等于y*j + i/gcd(i,j),则x - 1个,则数量等于x*i-j/gcd(i,j),最后把x调整到[0,cnt[i]]内就可以,然后ans=min(ans,num_cnt(中间选的)+ x + y)

        (3)代码实现

// Problem: Ginger的数
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/49030/F
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include "bits/stdc++.h"
#define rep(i, z, n) for (int i = z; i <= n; i++)
#define per(i, n, z) for (int i = n; i >= z; i--)
#define ll long long
#define db double
#define PII pair<int, int>
#define fi first
#define se second
#define vi vector<int>
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
using namespace std;
const int N = 1e5 + 10;
ll cnt[20];
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if (!b)
    {
        x = 1, y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, y, x);
    y -= a / b * x;
    return d;
}
void solve()
{
    vector<int> nums;
    ll n, m, t;
    cin >> n >> m;
    t = m - 1;
    while (m)
    {
        nums.push_back(m % 10);
        m /= 10;
    }
    ll pw = 1;
    ll ans = 9e18, x, y;
    for (int i = 0; i < nums.size(); i++)
    {
        if (i < nums.size() - 1)
        {
            cnt[i + 1] = 9 * pw;
        }
        else
        {
            cnt[i + 1] = t - pw + 1;
        }
        if (n % (i + 1) == 0 && n / (i + 1) <= cnt[i + 1])
        {
            ans = min(ans, n / (i + 1));
        }
        pw *= 10;
    }
    for (int i = 1, len = nums.size(); i <= len; i++)
    {
        for (int j = i + 1; j <= len; j++)
        {
            ll num = 0, num_cnt = 0;
            for (int k = i + 1; k < j; k++)
            {
                num += cnt[k] * k;
                num_cnt += cnt[k];
            }
            ll res = n - num;
            if (res < 0)
                continue;
            //第i位和第j位凑出res
            ll d = exgcd(i, j, x, y);
            if (res % d)
                continue;
            x *= res / d, y *= res / d;
            ll up1 = i / d, up2 = j / d;
            //将y的数量调到最大
            if (y > cnt[j])
            {
                ll ct = (y - cnt[j] + up1 - 1) / up1;
                y -= ct * up1;
                x += ct * up2;
            }
            else if (y < cnt[j])
            {
                ll ct = (cnt[j] - y) / up1;
                y += ct * up1;
                x -= ct * up2;
            }
            if (x < 0)
            {
                ll ct = (-x + up2 - 1) / up2;
                y -= ct * up1;
                x += ct * up2;
            }
            if (x >= 0 && x <= cnt[i] && y >= 0 && y <= cnt[j])
            {
                ans = min(ans, x + y + num_cnt);
            }
        }
    }
    if (ans == 9e18)
    {
        cout << "GingerNB" << endl;
    }
    else
    {
        cout << ans << endl;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

2.I-最大公约数求和_中国石油大学(北京)第三届“骏码杯”程序设计竞赛(同步赛) (nowcoder.com)

        (1)题目大意

         (2)解题思路

                考虑积数函数的性质f(xy) = f(x) * f(y),因为i^k是个完全积性函数,我们只需要考虑gcd(i,k),而且gcd(i,k)是个积性函数,若gcd(p,i)不互质,那么我们暴力跑一次,否则就可以进行递推。

         (3)代码实现

#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <stack>
#include <algorithm>
#include <cmath>
#include <unordered_map>
#include <deque>
#include <bitset>
#define rep(i,z,n) for(int i = z;i <= n; i++)
#define per(i,n,z) for(int i = n;i >= z; i--)
#define PII pair<int,int>
#define fi first
#define se second
#define vi vector<int>
#define vl vector<ll>
#define pb push_back
#define sz(x) (int)x.size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll = long long;
const int N = 2e7 + 10;
bitset <N> st;
int primes[N],cnt;
int f[N],g[N];
const ll mod = 1e9 + 7;
ll ksm(ll a,ll b)
{
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
void solve()
{
    int n;
    ll k;
    cin >> n >> k;
    ll ans = 1;
    g[1] = 1;
    rep(i,2,n) {
        if(!st[i]) {
            primes[++ cnt] = i;
            f[i] = ksm(i,k);
            g[i] = __gcd(1ll * i,k);
        }
        for(int j = 1;primes[j] <= n / i;j ++) {
            int x = primes[j] * i;
            f[x] = 1ll * f[i] * f[primes[j]] % mod;
            st.flip(x);
            if(i % primes[j] == 0) {
                g[x] = __gcd(x * 1ll,k);
                break;
            }
            g[x] = 1ll * g[i] * g[primes[j]] % mod;
        }
        ans = (ans + 1ll * f[i] * g[i]) % mod;
    }
    cout << ans << endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int T = 1;
    // cin >> T;
    while(T --) solve();
    return 0;
}

3.HDU - 4565 

        (1)题目大意

         (2)解题思路

                主要考虑怎么解决根号这个事情,注意到题目的一个条件(a-1)^2<b<a^2。我们假设我们求得是Cn,设An为整数项,Bn为小数项,根据共轭式,An + Bn一定是个整数,则Cn=An+Bn,An = (a + \sqrt{b})^{_{n}} ,Bn = (a - \sqrt{b})^{^{n}}

                令a + \sqrt{b} = t1,a - \sqrt{b} = t2,则Cn = t1^n + t2^n

                继而推出Cn * (t1 + t2) = (t1 ^ n + t2 ^ n) * (t1 + t2)

                化简一下得Cn * (t1 + t2) = t1 ^ {n + 1} + t2 ^ {n + 1} + t1^nt2 + t2^nt1

                再次化简得C_n * (t1 + t2) = C_{n+1} + (a ^ 2 + b) * C_{n - 1}

                移向得C_{n + 1} = 2aC_n - (a^2 - b)C_{n - 1}

                考虑构造系数矩阵​​​​​​​

                 然后矩阵快速幂写写就行了。

        (3)代码实现

#include "bits/stdc++.h"
#define ll long long
using namespace std;
 
struct Mat {
    ll dp[2][2],p;
     
    friend Mat operator *(Mat A,Mat B){
        Mat res;
        for(int i = 0;i < 2;i ++) {
            for(int j = 0;j < 2;j ++) {
                res.dp[i][j] = 0;
            }
        }
        res.p = A.p;
        for(int i = 0;i < 2;i ++) {
            for(int j = 0;j < 2;j ++) {
                for(int k = 0;k < 2;k ++) {
                    res.dp[i][j] += A.dp[i][k] * B.dp[k][j];
                    res.dp[i][j] %= res.p;
                }
            }
        }
        return res;
    }
};
 
Mat pow(Mat A,int b,int m)
{
    Mat res;
    res.dp[0][0] = 1;res.dp[0][1] = 0;
    res.dp[1][0] = 0;res.dp[1][1] = 1;
    res.p = m,A .p = m;
    while(b) {
        if(b & 1) res = res * A;
        A = A * A;
        b >>= 1;
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int a,b,n,m;
    while(cin >> a >> b >> n >> m) {
        if(n == 0) {
            cout << 2 % m << endl;
            continue;
        }
        if(n == 1) {
            cout << 2 * a % m << endl;
            continue;
        }
        Mat A;
        A.dp[0][0] = 2 * a % m;A.dp[0][1] = ((-(a * a - b)) % m + m) % m;
        A.dp[1][0] = 1;A.dp[1][1] = 0;A.p = m;
        A = pow(A,n - 1,m);
        ll ans = A.dp[0][0] * (2 * a % m) % m + A.dp[0][1] * 2 % m;
        ans %= m;
        cout << ans << '\n';
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值