牛客竞赛数学专题班整数分解与筛法

A

Solution

1.扩展欧几里得算法板子题。

题目提取式子: ( x + k m ) % L = ( y + k n ) % L (x+km)\%L=(y+kn)\%L (x+km)%L=(y+kn)%L

优化: x + k m = y + k n − v L x+km = y+kn - vL x+km=y+knvL

k ( m − n ) + L v = y − x k(m-n) + Lv = y-x k(mn)+Lv=yx

a x + b y = g c d ( a , b ) ax+by=gcd(a, b) ax+by=gcd(a,b)式子雷同,

求出x与y的一组解,如果y-x为a、b最大公因数的倍数则有解,否则无解。

Code

/*************************************
 * @problem:      problem title.
 * @author:       iamshroud.
 * @time:         2021-??-??.
*************************************/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
// #include <unordered_map>

typedef long long ll;

#define itn int //ovo
#define MOD 1000000007
#define intmax 2147483647
#define memmax 0x7fffffff

using namespace std;

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}


ll m, n, L;
ll a, b, x, y, c;

ll exgcd(ll a, ll b, ll& x, ll& y)
{
    if(!b)
    {
        x = 1;
        y = 0;
        return a;
    }

    ll d = exgcd(b, a%b, x, y);
    ll x2 = x;
    x = y;
    y = x2-a/b*y;

    return d;
}

void solve()
{
    ll x1, y1;
    cin >> x1 >> y1 >> m >> n >> L;

    a = m - n;
    b = L;
    c = y1-x1;

    if(a < 0)
    {
        a = -a;
        c = -c;
    }

    ll gg = exgcd(a, b, x, y);

    // c 为 gcd(a, b)的倍数,有解
    if(c%gg == 0)
    {
        // x = x*c/gg;

        L = L/gg;

        // 乘以倍数,使得答案对称
        x = (x*c/gg%L+L)%L;

        cout << x;
        return ;
    }

    cout << "Impossible";

}

int main(void)
{
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    // come on
    solve();

    


    return 0;
}

B

Solution

1.n的数据过大,用埃氏筛会超时,此处采用欧拉筛

2.对于查找答案,可以使用滑动窗口的思想。

由于前缀素数和具有单调递增的性质,对于寻找区间,left值可以先遍历到大于n的地方,再寻找对应的right。

Code

/*************************************
 * @problem:      problem title.
 * @author:       iamshroud.
 * @time:         2021-??-??.
*************************************/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
// #include <unordered_map>

typedef long long ll;

#define itn int //ovo
#define MOD 1000000007
#define intmax 2147483647
#define memmax 0x7fffffff

using namespace std;


int t;
int n;
bool vis[40000005];
int prime[40000005];
int cnt;
int pre[40000005];


void prework()
{
    for(int i=2; i<=40000000; i++)
    {
        if(!vis[i])
        {
            prime[++cnt] = i;
        }

        for(int j=1; j<=cnt && i*prime[j] <=40000000; j++)
        {
            vis[i*prime[j]] = true;
            if(i % prime[j] == 0)
                break;
        }
    }

    for(int i=1; i<=cnt; i++)
    {
        pre[i] = pre[i-1] + prime[i];
    }
}

void solve()
{
    ll ans = 0;
    

    for(int i=1, j=1; prime[i]<=n && j<=cnt; i++)
    {
        // 枚举j
        while(j<=cnt && pre[j]-pre[i-1] < n)
            j++;
        if(pre[j]-pre[i-1] == n)
            ans++;
    }

    // cout << "ans = ";

    cout << ans << endl;
    
}

int main(void)
{
    
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    prework();

    // cout << "cnt = " << cnt << endl;

    cin >> t;
    // come on
    while(t--)
    {
        cin >> n;

        solve();
    }

    


    return 0;
}

C

Solution

1.区间筛模板题

Code

/*************************************
 * @problem:      problem title.
 * @author:       iamshroud.
 * @time:         2021-??-??.
*************************************/
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
// #include <unordered_map>

typedef long long ll;

#define itn int //ovo
#define MOD 1000000007
#define intmax 2147483647
#define memmax 0x7fffffff

using namespace std;

inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}


ll n;
ll l, r;

bool is_prime_small[1000005];//用于线性筛[0, sqrt(b)]的
bool is_prime[1000005];// 真正的素数(偏移量为a)
ll prime[1000005];// 存储素数
ll cnt;

//区间筛
// [a, b] -> [0, b-a]
void segment_sieve(ll a, ll b)
{
    cnt = 0;

    // 假设全为素数,遍历到sqrt(b)即可
    for(ll i=2; i*i<=b; i++)
    {
        is_prime_small[i] = true;
    }

    // [a, b] 区间全部默认是素数
    for(ll i=0; i<=b-a; i++)
    {
        is_prime[i] = true;
    }

    // 特判1
    if(a==1)
        is_prime[1-a] = false;

    for(ll i=2; i*i<=b; i++)
    {
        if(is_prime_small[i])
        {
            // 线性筛思想
            for(ll j=2*i; j*j<=b; j+=i)
            {
                is_prime_small[j] = false;
            }

            // max(2ll, (i+a-1)/i) * i 
            // 求距离a最近,但比a大的数
            for(ll j=max(2ll, (i+a-1)/i)*i; j<=b; j+=i)
            {
                // j isn't a prime
                is_prime[j-a] = false;
            }
        }
    }

    // 记录答案
    for(ll i=0; i<=b-a; i++)
    {
        // a+i is prime
        if(is_prime[i])
            prime[++cnt] = a+i;
    }
}

void solve()
{
    segment_sieve(l, r);


    // for(int i=1; i<=cnt; i++)
    // {
    //     cout << "prime[" << i << "] = " << prime[i] << endl;
    // }

    if(cnt < 2)
    {
        printf("There are no adjacent primes.\n");
        return ;
    }
    
    ll mindis = intmax, maxdis = -1;
    ll minleft = 0, minright = 0;
    ll maxleft = 0, maxright = 0;

    for(int i=2; i<=cnt; i++)
    {
        ll dis = prime[i]-prime[i-1];
        if(dis < mindis)
        {
            mindis = dis;
            minleft = prime[i-1], minright = prime[i];
        }
        if(dis > maxdis)
        {
            maxdis = dis;
            maxleft = prime[i-1], maxright = prime[i];
        }
    }

    printf("%lld,%lld are closest, %lld,%lld are most distant.\n", minleft, minright, maxleft, maxright);


}

int main(void)
{
    
    // come on
    ll t;
    cin >> t;
    while(t--)
    {
        cin >> l >> r;
        solve();
    }
    // while(scanf("%lld%lld", &l, &r) == 2)
    //     solve();



    return 0;
}

D

Solution

1.先线性筛选出质数

2.高到低遍历质数,能除n就除n

Code

/*************************************
 * @problem:      cf_init.cpp.
 * @author:       iamshroud.
 * @time:         2021-11-02. 周二
*************************************/

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <iomanip>
#include <list>

using namespace std;

typedef long long ll;

#define MOD 1000000007
#define intmax 2147483647
#define memmax 0x7fffffff


ll quickPow(ll n,ll p,ll k=intmax)
{
    ll ans = 1;
    ll base = n;
    
    while (p)
    {
        //最后一位为1
        if(p&1)
        {
            ans *= base;
            ans %= k;
        }
	   //去掉一位数
        base *= base;
        base %= k;
        p >>= 1;
    }
    
    return ans;
}

ll t;
ll k;

bool isprime[30005];
ll prime[30005];
ll cnt;

void predeal()
{
    for(int i=2; i<=30000; i++)
    {
        isprime[i] = true;
    }

    for(int i=2; i*i<=30000; i++)
    {
        if(isprime[i])
        {
            for(int j=2*i; j<=30000; j+=i)
            {
                isprime[j] = false;
            }
        }
    }

    for(int i=2; i<=30000; i++)
    {
        if(isprime[i])
            prime[++cnt] = i;
    }

    // for(int i=cnt; i>=cnt-20; i--)
    // {
    //     cout << prime[i] << ' ';
    // }
    // cout << endl;

    
}

void solve()
{
    // cout << "ans == ";

    // init
    ll n = 1;

    cin >> k;
    for(int i=1; i<=k;i++)
    {
        ll p, e;
        cin >> p >> e;
        n *= quickPow(p, e);
    }

    // cout << "n = " << n << endl;

    n--;
    for(int i=cnt; i>=1; i--)
    {
        if(prime[i] > n)
            continue;
        if(n == 1)
            break;

        ll num = 0;
        while(n % prime[i] == 0)
        {
            num++;
            n /= prime[i];

        }
        if(num)
            cout << prime[i] << ' ' << num << " ";
    }
   

    // for(int i=cnt; i>=1; i--)
    // {
    //     cout << arr[i].id << " " << arr[i].value << " ";
    // }
    cout << endl;
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    predeal();
   
    cin >> t;
    while (t--)
    {
        solve();
    }
    
    return 0;
}

E

Solution

1.原本是想欧拉筛加记录质因数,再统计相同的数字的个数再计算答案,但发现超时了,需要一些手段。

2.可以使用一个minprime[i]数组来记录i的最小质因数,这样在分解x的质因数时速度会快很多,最坏情况 O ( n ) − > O ( 1 ) O(n)->O(1) O(n)>O(1)

3.将x分解成质因数形式后,简化排列组合计算。

显然,令 x = p 1 a 1 p 2 a 2 . . . . p k a k x=p_1^{a_1}p_2^{a_2}....p_k^{a_k} x=p1a1p2a2....pkak

则m的最大值为 ∑ i = 1 k a i \sum_{i=1}^k a_i i=1kai

方案数,通过排列组合思想,为

C m a 1 C m − a 1 a 2 . . . C m − a 1 − . . . − a k − 1 a k C_{m}^{a_1}C_{m-a_1}^{a_2}...C_{m-a_1-...-a_{k-1}}^{a_k} Cma1Cma1a2...Cma1...ak1ak

C m n   =   m ! n ! ( m − n ) ! C_m^n\ =\ \frac{m!}{n!(m-n)!} Cmn = n!(mn)!m!

上述式子可化为

m ! a 1 ! ( m − a 1 ) ! ( m − a 1 ) ! a 2 ! ( m − a 1 − a 2 ) ! . . . ( m − a 1 − a 2 − . . . a k − 1 ) ! a k ! ( m − a 1 − a 2 − . . − a k ) ! \frac{m!}{a_1!(m-a_1)!}\frac{(m-a_1)!}{a_2!(m-a_1-a_2)!}...\frac{(m-a_1-a_2-...a_{k-1})!}{a_k!(m-a_1-a_2-..-a_k)!} a1!(ma1)!m!a2!(ma1a2)!(ma1)!...ak!(ma1a2..ak)!(ma1a2...ak1)!

由于 ( m − a 1 − a 2 − . . − a k ) !   =   1 (m-a_1-a_2-..-a_k)!\ =\ 1 (ma1a2..ak)! = 1

上述式子化简得

m ! a 1 ! a 2 ! . . . a k ! \frac{m!}{a_1!a_2!...a_k!} a1!a2!...ak!m!

由数据得,m最多不超过20,预处理好前20的阶乘即可。

Code

/*************************************
 * @problem:      cf_init.cpp.
 * @author:       iamshroud.
 * @time:         2021-11-02. 周二
*************************************/

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <string>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <iomanip>
#include <list>

using namespace std;

typedef long long ll;

#define MOD 1000000007
#define intmax 2147483647
#define memmax 0x7fffffff

ll t;
ll x;

bool not_prime[1000005];
ll prime[100005];
ll minprime[1000005];//这个数的最小质因数
ll cnt;

ll pre[30];

void predeal()
{
    const int maxn = 1000000;

    for(int i=2; i<=maxn; i++)
    {
        if(!not_prime[i])
        {
            prime[++cnt] = i;
            minprime[i] = i;
        }
        for(int j=1; j<=cnt && i*prime[j]<=maxn; j++)
        {
            not_prime[i*prime[j]] = true;
            minprime[i*prime[j]] = prime[j];//记录最小质因数
            if(i % prime[j] == 0)
                break;
        }
    }


    pre[0] = 1;
    for(ll i=1; i<=25; i++)
        pre[i] = pre[i-1]*i;
}


inline void solve()
{
    scanf("%lld", &x);

    // cout << "ans = ";

    // spj
    if(!not_prime[x])
    {
        printf("1 1\n");
        return ;
    }

    ll m = 0;
    vector<int>v;

    while(x != 1)
    {
        ll tmp = 0;

        while(x % minprime[x] == 0)
        {
            m++;
            tmp++;
            x /= minprime[x];
        }

        if(tmp)
            v.push_back(tmp);
    }


    ll num = pre[m];
    for(int i=0; i<v.size(); i++)
    {
        num /= pre[v[i]];
    }    

    printf("%lld %lld\n", m, num);

}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    predeal();
   
    scanf("%lld", &t);
    while (t--)
    {
        solve();
    }
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值