2017 暑假艾教集训 day4

本文解析了多项数论算法竞赛题目,包括欧拉函数、快速幂、离散对数等核心概念及其应用,提供了清晰的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 题库: https://cn.vjudge.net/contest/176263#overview

A:HDU 40704

做法:相当于将 n 任意组合, 可能的情况为 2^(n-1),因为n会很大,使用欧拉降幂公式


B:Given A,B,C, You should quickly calculate the result of A^B mod C. (1<=A,C<=1000000000,1<=B<=10^1000000).

做法:同上题一样算出C的欧拉数,做欧拉降幂


C:BZOJ 3884

做法:递归欧拉降幂。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll getoula(ll n)
{
    if(n==1) return 0;
    ll ans=n;
    for(int i=2;i<=(int)sqrt(n+0.5);++i)
    {
        if(n%i==0)
        {
            ans = ans * (i-1) /i;
            while(n%i==0) n/=i;
        }
    }
    if(n>1) ans= ans * (n-1) /n;
    return ans;
}

ll powermod(ll bit ,ll n, ll mod)
{
    ll ans=1;
    while(n)
    {
        if(n & 1) ans = (ans * bit) %mod;
        bit = (bit * bit )%mod;
        n>>=1;
    }
    return ans%mod;
}
ll f(ll p)
{
    if(p==1) return 0;
    int temp=getoula(p);
    return powermod(2,f(temp)+temp,p);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll p;
        scanf("%lld",&p);
        printf("%lld\n",f(p));
    }
    return 0;
}

D POJ 2407

做法: 欧拉数水题

E POJ 2478

做法:筛发求欧拉数前缀和

F POJ 1845

做法:不难发现对于每个合适的sum等于其所有素数的等比数列和的积。

素数唯一定理,素数分解,等比数列递归求和,快速幂

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod = 9901;
ll powermod(ll bit,ll n,ll mod)
{
    bit=bit % mod;
    ll ans=1;
    while(n)
    {
        if(n & 1) ans = (ans * bit) %mod;
        bit = (bit * bit) %mod;
        n>>=1;
    }
    return ans;
}

ll sum(ll p ,ll n,ll mod)
{
    if(p==0) return 0;
    if(n==0) return 1;
    if(n&1) return (  ( 1+powermod(p,n/2+1,mod) )%mod * sum(p,n/2,mod)%mod )%mod;
    else return ( (1+powermod(p,n/2+1,mod)) %mod *sum(p,n/2-1,mod)+powermod(p,n/2,mod)%mod)%mod;
}

const int maxn=10000;
bool book[maxn+10];
int prim[maxn+10],pnum=0;
void getprim()
{
    memset(book,0,sizeof(book));
    pnum=0;
    book[1]=book[0]=1;
    for(int i=2;i<=maxn;++i)
    {
        if(!book[i])
        {
            prim[pnum++]=i;
        }
        for(int j=0;j<pnum && prim[j]<=maxn/i ;++j)
        {
            book[prim[j]*i]=1;
            if(i % prim[j]==0) break;
        }
    }
}

ll fact[105][2];
int fnum=0;
int getfact(ll x)
{
    fnum=0;
    ll temp=x;
    for(int i=0;prim[i]<=temp/prim[i];++i)
    {
        fact[fnum][1]=0;
        if(temp%prim[i]==0)
        {
            fact[fnum][0]=prim[i];
            while(temp % prim[i]==0)
            {
                fact[fnum][1]++;
                temp /=prim[i];
            }
            fnum++;
        }
    }
    if(temp!=1)
    {
        fact[fnum][0]=temp;
        fact[fnum++][1]=1;
    }
    return fnum;
}

int main()
{
    ll a,b;
    getprim();
    while(scanf("%lld%lld",&a,&b)!=EOF)
    {
        getfact(a);
        ll ans=1;
        for(int i=0;i<fnum;++i)
        {
            ans = ( ans * sum( (ll)fact[i][0],(ll)b*fact[i][1],mod)%mod )%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

G POJ 1811
做法:大数的素数检验,如果是合数的话算出最小质因
模板:Miller_rabin + Pollard_rho



K POJ 1284
做法:欧拉数的应用, 一个存在原根的数的原根数 等于 Phi(Phi(n));


M POJ 3090
做法:很容易发现,图关于y=x这条线对称。 且 每次增加的为与前项gcd(i,n)为1的个数
ans = 1 +2* Sigmn Phi[i]。



N  SPOJ 7001
题意:求GCD(X,Y,Z)=1 的种数 。
做法: 莫比乌斯函数反演,设 g(m)= GCD==x 的和 。 f(m) = GCD==kx 的和。 满足第二种莫比乌斯反演的形式
也不难发现 f(m)= [n/m]^3


 记着考虑 X,Y,Z有 一个0 两个 0的情况

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=1000000;
bool book [maxn+5];
int prim[maxn+5];
int cnt=0;
int mu[maxn+5];
void getprim()
{
    memset(book,1,sizeof(book));
    cnt=0;
    mu[1]=1;
    for(int i=2;i<maxn ;++i)
    {
        if(book[i])
        {
            prim[cnt++] = i; mu[i]= -1;
        }
        for(int j=0;j<cnt && i*prim[j]<=maxn;++j)
        {
            book[i*prim[j]]=0;
            if(i % prim[j]==0)
            {
                mu[i*prim[j]]=0;
                break;
            }
            else
            {
                mu[i*prim[j]]= -mu[i];
            }
        }
    }

}
int main()
{
    getprim();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);

        ll ans=3;
        for(int i=1;i<=n;++i) ans +=(ll) mu[i]*(n/i)*(n/i)*(n/i + 3);
        printf("%I64d\n",ans);
    }
    return 0;
}

HDU 1695

做法:求GCD(X,Y)==K 的种数 X属于(1,b) Y属于 (1,d)中 两边同除K。问题转换为 (1,b/K)(1,d/K)

求 GCD(x,y)==1;  和上一道题一样 直接莫比乌斯反演。

最后记着把 重复 的去掉

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100003;
bool book [maxn+5];
int p[maxn+5];
int mu[maxn+5];
int cnt=0;
void getprime()
{
    memset(book,1,sizeof(book));
    cnt=0;
    mu[1]=1;
    for(int i=2;i<maxn;++i)
    {
        if(book[i])
        {
            p[cnt++]=i; mu[i]= -1;
        }
        for(int j=0; j<cnt && i*p[j]<maxn;++j)
        {
            book[i*p[j]]=0;
            if(i %p[j]==0)
            {
                mu[i *p[j]]= 0;
                break;
            }
            else mu[i*p[j]] = -mu[i];
        }
    }
}
int main()
{
    getprime();
    int T;
    int kiss=0;
    scanf("%d",&T);
    while(T--)
    {
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0)
        {
            printf("Case %d: 0\n",++kiss);
            continue;
        }
        int x=(b/k);
        int y=(d/k);

        ll ans1=0;
        for(int i=1;i<=min(x,y);++i)
        {
            ans1 = ans1 +(ll) mu[i] * (x/i) * (y/i);
        }

        ll ans2=0;
        for(int i=1;i<=min(x,y);++i)
        {
            ans2 =ans2 +(ll) mu[i] * (min(x,y)/i) *(min(x,y)/i);
        }
        printf("Case %d: %I64d\n",++kiss,ans1-ans2/2);
    }
    return 0;
}

POJ 2417

做法:裸的离散对数 (BSGS)。





### 关于雅礼集训 2017 Day1 的题目及解析 #### 题目概述 根据已知引用内容[^3],雅礼集训 2017 Day1 的核心问题是关于矩阵操作的优化问题。给定一个 \(n \times m\) 的字符矩阵,其中 `#` 表示黑色格子,`.` 表示白色格子。目标是最小化将整个矩阵变为全黑所需的步数。 --- #### 解析与算法思路 ##### 输入描述 输入的第一行为两个整数 \(n\) 和 \(m\),分别代表矩阵的行数和列数。接下来 \(n\) 行每行包含长度为 \(m\) 的字符串,表示矩阵的内容。 ##### 输出描述 输出最小的操作次数使得整个矩阵变成全是黑色格子的状态。如果没有可行方案,则输出 `-1`。 --- ##### 算法设计 1. **可行性判断** 如果初始矩阵没有任何黑色格子 (`#`) 存在,则无法通过任何有限次操作使矩阵变黑,因此直接返回 `-1`[^4]。 2. **计算最少步数** 对于每一行,定义两种可能的操作方式: - 将该行全部涂黑。 - 不改变该行状态,仅依赖后续列操作来覆盖剩余白格。 同样地,对于每一列也存在类似的策略选择。最终的目标是综合考虑行列操作的影响,找到全局最优解。 3. **动态规划或贪心求解** 使用简单的遍历方法统计各行列中的黑白分布情况,并基于此决定最佳行动顺序。特别注意边界条件处理以及特殊情况下的额外开销评估。 ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e3 + 5; int n, m, h[MAXN], l[MAXN]; char s[MAXN][MAXN]; int main(){ cin >> n >> m; bool has_black = false; for (int i = 1; i <= n; ++i){ cin >> (s[i]+1); for (int j = 1; j <= m; ++j){ if (s[i][j] == '#'){ has_black = true; h[i]++; l[j]++; } } } if (!has_black){ cout << "-1"; return 0; } int res = INT_MAX; for (int i = 1; i <= n; ++i){ res = min(res, m - h[i] + !l[i]); } int extra_cost = 0; for (int j = 1; j <= m; ++j){ if (l[j] != n) extra_cost += 1; } cout << res + extra_cost; } ``` 上述代码实现了基本逻辑框架,包括读取数据、初步分析是否存在解决方案的可能性以及最后一步汇总总成本的过程[^3]。 --- #### 复杂度分析 时间复杂度主要取决于两次嵌套循环扫描整个矩阵所需的时间量级 O(n*m),空间消耗同样维持在线性范围内 O(n+m)。 --- #### 注意事项 - 当前实现假设所有测试实例均满足合理范围内的尺寸规格;实际应用时需增加更多健壮性的错误检测机制。 - 结果验证阶段应充分考虑到极端情形比如完全空白或者满布障碍物等情况是否被妥善处置。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值