Day5 数论
一些定义和性质
,
只有 种取值
数论函数:定义域为正整数,陪域为复数的函数。我们主要研究定义域为正整数,值域为整数的函数。
积性函数:满足 若a, b互质,则 的数论函 数称为积性函数。
完全积性函数:满足 的数论函数称为完全积性函数
狄利克雷卷积:对于数论函数,定义其狄利克雷卷积
两个积性函数的狄利克雷卷积仍为积性函数
一些常见的积性函数
- 单位函数
- 常函数
- 幂函数
- 欧拉函数 代表[1,x]中与x互质的个数=
莫比乌斯函数
这是莫比乌斯函数最基本的定义,可以用这个可以将“等于1的数量”转化为莫比乌斯函数
杜教筛
求 ,其中 是一个积性函数。
构造一个积性函数 ,那么由 ,得到 。
当然,要能够由此计算 ,会对 提出一些要求:
- 要能够快速求前缀和。
- 要能够快速求分段和(前缀和)。
- 对于正常的积性函数 ,所以不会有什么问题。
在预处理 前 项的情况下复杂度是
A - HDU 4965
两个矩阵A,B,求(A*B)的N*N次幂。其中元素都%6 因为N范围是1000,K范围是6,如果直接对A * B快速幂的话是对一个1000*1000的矩阵操作,会TLE。所以可以先求出*A的(N*N-1)次方,这是一个5*5的矩阵,再右乘A左乘B
#include
using namespace std;
struct Matrix{
vector> v;
int n, m;
Matrix(int n,int m,int op=0):n(n),m(m){
v.resize(n);
for(auto& e:v){
e.resize(m, 0);
}
if(op){
for (int i = 0; i < n && i < m;i++){
v[i][i] = 1;
}http://acm.hdu.edu.cn/showproblem.php?pid=4965
}
}
friend Matrix operator *(const Matrix& mat,const Matrix& other){
Matrix ret = Matrix(mat.n, other.m);
for (int i = 0; i < mat.n; i++)
{
for (int j = 0; j < mat.m;j++){
for (int k = 0; k < other.m;k++){
ret.v[i][k] += mat.v[i][j] * other.v[j][k];
ret.v[i][k] %= 6;
}
}
}
return ret;
}
Matrix pow(int t) const{
Matrix ret = Matrix(this->n,this->n,1);
Matrix tmp = *this;
for (; t; t >>= 1,tmp=tmp*tmp)
if(t&1)
ret = ret * tmp;
return ret;
}
int sum() const{
int ret = 0;
for(auto& c:this->v){
for(auto& e:c){
ret += e;
}
}
return ret;
}
};
int main(int argc,char const *argv[]){
int n, m;
while(scanf("%d%d", &n, &m)){
if(n==0&&m==0)
break;
Matrix a(n, m);
Matrix b(m, n);
for (int i = 0; i < n;i++){
for (int j = 0; j < m;j++){
scanf("%d", &a.v[i][j]);
}
}
for (int i = 0; i < m;i++){
for (int j = 0; j < n;j++){
scanf("%d", &b.v[i][j]);
}
}
auto ans = (a * ((b * a).pow(n*n - 1))) * b;
printf("%d\n", ans.sum());
}
}
B - UVA 10655
题意:给出两个数字代表a+b和a*b,再给一个数字n,求
题解:首先直接把这两个值带入二次方程求解是错误的,因为a和b可以为复数,如 与 ,我们无法处理这种情况
考虑递推公式:
令,于是有
然后又是一个矩阵快速幂
#include
using namespace std;
using ll = long long;
struct Mat{
ll m[2][2];
};
Mat a,e;
const int n = 2;
Mat Mul(Mat x, Mat y)
{
Mat c;
for(int i=0;i>=1;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);
long long p, q;
int an;
while (cin >> p >> q)
{
if(!(cin>>an)){
break;//这里wa了很多发,注意终止条件
}
a.m[0][0] = p;
a.m[0][1] = -q;
a.m[1][0] = 1;
a.m[1][1] = 0;
if (an == 0)
{
cout << 2 << endl;
continue;
}
if(an==1){
cout << (long long)p << endl;
continue;
}
if(an==2){
cout << p * p - 2 * q << endl;
continue;
}
an-=2;
a=pow(a, an);
long long ans = a.m[0][0] * p + a.m[0][1] * 2;
cout << (long long)ans << endl;
}
return 0;
}
C - UVA 1386
题意:给定一个n格的环,现在有个距离d,每次变化把环和他周围距离d以内的格子相加,结果mod m,问经过k次变换之后,环上的各个数字
题解:写成矩阵乘法形式很好想:距离d以内的为1,别的都是0。但是矩阵乘法直接会T,需要用循环矩阵优化到
refer
D - HDU 5680
题意: 满足 ,求 , N<1e9, 且只有5个测试大于1e6
题解:杜教筛裸题
#include
#define FOR(i, x, y) for (decay::type i = (x), _##i = (y); i < _##i; ++i)
using namespace std;
using LL = long long;
const LL mod = 1e9 + 7;
LL bin(LL x, LL n, LL MOD) {
LL ret = MOD != 1;
for (x %= MOD; n; n >>= 1, x = x * x % MOD)
if (n & 1) ret = ret * x % MOD;
return ret;
}
inline LL get_inv(LL x, LL p) { return bin(x, p - 2, p); }
const LL inv3 = get_inv(3, mod);
LL mul(LL a,LL b){
return ((a % mod) * (b % mod)) % mod;
}
LL add(LL a,LL b){
return (a + b + mod) % mod;
}
namespace dujiao {
const int M = 1E6;
LL mu[M] = {0, 1};
LL f[M];
inline LL g(LL n){
return (1ll * n * n % mod - 3ll * n % mod + 2) % mod;
}
void init()
{
static bool vis[M];
static LL pr[M], p_sz, d;
//先利用莫比乌斯函数暴力算前sqrt(N)项前缀和
FOR (i, 2, M) {
if (!vis[i]) { pr[p_sz++] = i; mu[i-] = -1; }
FOR (j, 0, p_sz) {
if ((d = pr[j] * i) >= M) break;
vis[d] = 1;
if (i % pr[j] == 0) {
mu[d] = 0;
break;
} else mu[d] = -mu[i];
}
}
FOR(i, 1, M){
for (int j = i; j < M; j += i)
{
f[j] = (f[j] + 1ll * mu[j / i] * g(i) % mod) % mod;
}
}
FOR (i, 2, M) f[i] = (f[i]+ f[i - 1])%mod;
}
inline LL s_fg(LL n) {
n = n % mod;
return inv3 * n % mod * ((n * n % mod - 3 * n % mod + 2 + mod) % mod) % mod;
}
inline LL s_g(LL n) {
return n % mod;
}
LL N, rd[M];
bool vis[M];
LL go(LL n) {
if (n < M) return f[n];
LL id = N / n;
if (vis[id]) return rd[id];
vis[id] = true;
LL& ret = rd[id] = s_fg(n);
for (LL l = 2, v, r; l <= n; l = r + 1) {
v = n / l; r = n / v;
ret = add(ret, - mul(add(s_g(r) , - s_g(l - 1)),go(v)));
}
return ret;
}
LL solve(LL n) {
N = n;
memset(vis, 0, sizeof vis);
return go(n);
}
}
int main(int argc, char const *argv[])
{
using namespace dujiao;
init();
int round, n;
scanf("%d", &round);
for (int i = 0; i < round;i++)
{
scanf("%d", &n);
printf("%lld\n", (solve(n) + mod) % mod);
}
return 0;
}
E - HDU 5942
题意:定义 为k的素因子个数, ,给一正整数n, ,求
题解:令,则,即为集合的子集个数,该集合一个子集的乘积p与该子集的补集(对全集合的补)的乘积q显然互素,且显然一一对应(k的任一互质的一对因子都可以拆成某个子集所有元素的积,和他的补的所有元素的积),也就是 ,记
记,于是
记忆S(n),然后枚举d即可,复杂度难以分析QAQ,另外这题时限15s,但是也卡常(比如分块计算S(n)就疯狂tle,后来还是oeis找到的严格 公式)
#include
using namespace std;
using ll = long long;
const ll maxn = 1e6 + 5;
const ll mod = 1e9 + 7;
ll mu[maxn], S[maxn];
void get_mu()
{
mu[1] = 1;
static bool vis[maxn];
static ll prime[maxn], sz, d;
for (ll i = 2; i < maxn;i++){
if(!vis[i]){
prime[sz++] = i;
mu[i] = -1;
}
for (ll j = 0; j < sz && (d = i * prime[j]) < maxn;j++){
vis[d] = 1;
if(i%prime[j]==0){
mu[d] = 0;
break;
}
else
mu[d] = -mu[i];
}
}
}
ll getS(ll n){
ll res = 0;
ll sq = sqrt(n);
for (int i = 1; i <= sq; i++)
{
res += n / i;
res %= mod;
}
res = (res)*2ll-sq *sq;
res = (res + mod) % mod;
return res;
}
ll Sum(ll n){
if (n < maxn&&S[n]!=-1) return S[n];
ll res = getS(n);
if(n
F - BZOJ 2818
题意:给定整数N,求1<=x,y<=N且gcd(x,y)为素数的数对(x,y)有多少对.
题解:对于某个质数p,如果gcd(x,y)=p,等价于gcd(x/p,y/p)=1。要求满足这等式的数量,不妨令x 于是对于每个质数,就是 , 其中p是枚举的每个质数,* 2是因为我们假定了x 接下来处理一下欧拉函数的前缀和即可。 (这个代码跑了7s) 解法二:令 , , 非常显然,有 (g(n)=f(n)+f(2n)+f(3n)+…),并且 (n|i并且n|j,就是这么多)。 这样就可以莫比乌斯反演,得到 , 于是 , 然后把g(d) 的部分提到外面,就得到然后预处理 即可: (这个代码跑了9580ms) 题意:给一个矩阵A,矩阵的元素要么是0要么是1,接下来有两个操作:区间修改和单点查询,区间修改是对一个矩形区域取反 题解:二位树状数组,维护差分数组,取反就是+1,然后所有运算在%2意义下。 题意:给一个椭圆(参数a,b)和两个值l和r(-a <= l <= r <= a),以l和r作两条垂直x轴的直线,求这两条直线与椭圆围成曲边矩形的面积 题解:辛普森公式 注意:刚开始的时候疯狂tle因为公式写的太丑了。。。必要的化简应该被考虑#include
for(int i:prime){//枚举质数
for(int j=1;i*j<=N;j++){//枚举d/n
sum[i*j]+=mu[j]
}
}
#include
G - POJ 2155
#include
H - HDU 1724
#include