数论
gcd相关
acwing 1246 等差数列
公差其实就是所有数减去第一个数的最大公约数。
首先,让0和其他数gcd,返回的是那个数本身。
其次,对于求abc的gcd,就是gcd(gcd(a,b),c)。
int d = 0;
for(int i = 1; i < n; i ++)
d = gcd(d, a[i] - a[0]);
质数相关
acwing1295 X的因子链
数据范围问题:220=1e6左右
要让因子链最长,那么a[i]/a[i-1]就要最小,最小一定是质数。
根据质因子分解的公式,最多有c1+c2+…+ck个质因子
也就是序列最大的长度。
而最大长度的种数,就是各种质因子组合起来的排列数。
由于质因子有重复,重复的就是等价的。需要用总的排列数去掉等价的排列数。每一组中排列数等价的有c1!c2!..ck!个(多重集的排列数问题)。
最后结果就是所有种数,除以每组的重复个数。
(c1+c2+…+ck)!/c1!c2!..ck!
acwing 1296 聪明的燕姿
约数之和S = (1+p1+p12+…+p1a1)(1+p2+p22+…+p2a2)…(1+pn+pn2+…+pnan)
如果从头质因数分解看是否满足约数之和为S肯定会超时。
用dfs进行搜索,得到符合条件的S%(1+pk+pk2+…+pkak)==0
大体框架:
if(S==1)
//输出结论
return;
for(p : 2,3,5,7,...)//最大sqrt(n)
for(a : 1,2,3,...)//只要加起来不超过S
if(S mod (1+p1+p1^2+...+p1^a1) == 0)
{
S=S/(1+p1+p1^2+...+p1^a1)
dfs(下一层)
S=S*(1+p1+p1^2+...+p1^a1)
}
Acwing 198 反素数
这道题也是分解质因子dfs
题目很玄乎,等价于求1到N中约数个数最多的,最小的x。
从给出的数据范围可以看出,最多有9个不同质因子,每个质因子最多30次方
我们用dfs确定前9个质数的指数(因为我们找的是不超过N的约数个数最多的,选择大的质数不如选择小的质数,因此质数的指数是单调递减的),满足的所有方案中取首先约数个数最多,其次最小的x即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll primes[11]={0,2,3,5,7,11,13,17,19,23,29};
int c[15];
int a,ans=0;
ll res=0x3f;
void dfs(int n,ll mul,int cnt)
{
if(n==11)
{
if(cnt>ans||(cnt==ans&&mul<res))
{
ans=cnt;
res=mul;
}
return;
}
ll t=mul;
for(int i=0;i<=c[n-1];i++)
{
if(t>a) return;
c[n]=i;
dfs(n+1,t,cnt*(i+1));
t*=primes[n];
}
}
int main()
{
cin>>a;
c[0]=0x3f;
dfs(1,1,1);
cout<<res;
return 0;
}
leetcode杂题
leetcode 264 丑数2
用小根堆实现。
首先把最小的丑数1入队。之后对于每个队列里的数,2x、3x、5x分别入队。第n个出队的就是第n大的丑数。
leetcode 313 超级丑数
其实是一样的,只不过质数列表不再是2,3,5了
leetcode 319 灯泡开关
看他亮还是不亮,其实就是看这个数有多少个因子。
每个平方数都有奇数个因子,非平方数都有偶数个因子。因此只有平方数是亮着的,答案是sqrt(n)[这是个下取整的结果]
leetcode 343 整数拆分
在4以内拆成2最好,在4以上都是3最好。(这是个结论)
n%3==0 拆成3
n%3==2 拆成3和一个2
n%3==1 拆成3和两个2
leetcode 365 水壶问题
ax+by=z
有解,当且仅当z是gcd(a,b)的倍数。
另外,
用深搜可以搜索出z是否满足,本题总共就4个操作,都跑一遍就行了。但是为了使得程序有出口,我们让他搜到重复状态就return(用hash表存储状态判重)。
leetcode 368 最大整除子集
用DP解决数论的整除问题
f[i]表示以第i个数字结尾的整除子集的长度
1.如果在i之前找不到nums[j]%nums[i]==0
则他作为一个全新的整除集合f[i]=1
2.如果找到了,那么就寻找满足条件的f[j]的最大值
f[i]=f[j]+1
同时要记录dp的具体方案,用g[]数组存储
如果f[i]=f[j]+1
那么g[i]=j
leetcode149 直线上最多的点数
当一个点固定了,那么在同一条直线上的点可以用他们和这个点之间的斜率来唯一标识。
leetcode166 分数到小数
小数的循环节怎么确定呢?当给出一个分数形式,其实就是一个除法形式,某一时刻的被除数相同,标志着进入了一个循环节。(注意分数如果化为无限小数的话,一定是循环的)。
把除法中每一个时刻的被除数都记录下来放入hash表里,到循环节开始的时候就可以用判重知晓了。
leetcode 224 基本计算器
用栈做计算器,分为一个符号栈,一个数字栈。
欧拉函数相关
小于n的数中与n互质的数的个数。
讲欧拉函数的很好的博客
求法代码
有点像埃氏筛,但是不同的是每筛出一个质数就用欧拉函数的公式计算一下。别忘了res从n开始,因为欧拉函数的公式要先乘n的。
int res=n;
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
res=res/i*(i-1);
while(n%i==0)
{
n/=i;
}
}
}
if(n>1)
{
res=res/n*(n-1);
}
cout<<res<<endl;