bzoj 1053
比较经典的一道题。
首先要观察出一些结论
(1)质数不超过10个。前十个质数相乘已经超过最大值。
(2)质数的指数是递减的。如果不是,可以把指数小的和大的交换一下,答案更小。
然后就搜索就好了。
这是一类数论+搜索的题目,要从题目看出一些剪枝,然后搜索即可。
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
ll a[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
ll n, ans, num;void dfs(int i, ll now, ll cnt, ll sum)
{if(i == 11) return;ll t = 1;_for(j, 1, cnt){t *= a[i];ll cur = now * t;if(cur > n) return;if(sum * (j + 1) == num && cur < ans)ans = cur, num = sum * (j + 1);else if(sum * (j + 1) > num)ans = cur, num = sum * (j + 1);dfs(i + 1, cur, j, sum * (j + 1));}
}int main()
{scanf("%lld", &n);dfs(1, 1, 31, 1);printf("%lld\n", ans);return 0;
}
bzoj 1257
这道题用到了一个很重要的结论
任意i属于[i, k / (k / i)], k / i的值相等(除法均是向下取整)
那么我们把k mod i 拆成 k - k / i * i,然后等差数列求和即可。
最后注意k/i可能会等于0,要特判一下
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
ll n, k;inline ll cal(ll l, ll r) { return (l + r) * (r - l + 1) / 2; }int main()
{scanf("%lld%lld", &n, &k);ll ans = n * k, sum = 0, l, r;for(int i = 1; i <= n; i = r + 1){l = i, r = k / i ? min(k / (k / i), n) : n;sum += (k / i) * cal(l, r);}printf("%lld\n", ans - sum);return 0;
}
Hankson 的趣味题
看到x 和 b0的最小公倍数是 b1
那就枚举b1的因子作为x,然后看满不满足条件。
注意枚举因子的时候不要把完全平方数的因子算两遍
557ms
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
int lcm(int a, int b) { return a / gcd(a, b) * b; }int main()
{int T;scanf("%d", &T);while(T--){int a0, a1, b0, b1;scanf("%d%d%d%d", &a0, &a1, &b0, &b1);int m = sqrt(b1 + 0.5), ans = 0;_for(x, 1, m)if(b1 % x == 0){if(lcm(x, b0) == b1 && gcd(x, a0) == a1) ans++;if(x != b1 / x){int tmp = b1 / x;if(lcm(tmp, b0) == b1 && gcd(tmp, a0) == a1) ans++;}}printf("%d\n", ans);}return 0;
}
但是算法竞赛进阶指南上写枚举因子可以优化。
先打出素数表,然后质因数分解,然后用搜索组成因子
这种方法的确快了很多,196ms
注意质因数分解的时候注意可能最后有剩下一个大质数,要判断一下
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;const int MAXN = 1e5 + 10;
bool is_prime[MAXN];
vector<int> prime, ve;
int a[15], m, ans;
int a0, a1, b0, b1;void get_prime()
{memset(is_prime, true, sizeof(is_prime));is_prime[0] = is_prime[1] = false;REP(i, 2, MAXN){if(is_prime[i]) prime.push_back(i);REP(j, 0, prime.size()){if(i * prime[j] >= MAXN) break;is_prime[i * prime[j]] = false;if(i % prime[j] == 0) break;}}
}int gcd(int a, int b) { return !b ? a : gcd(b, a % b); }
int lcm(int a, int b) { return a / gcd(a, b) * b; }inline bool check(int x)
{return lcm(x, b0) == b1 && gcd(x, a0) == a1;
}void deal()
{ve.clear();memset(a, 0, sizeof(a));m = 0;int t = b1;REP(j, 0, prime.size())if(t % prime[j] == 0){ve.push_back(prime[j]);while(t % prime[j] == 0){t /= prime[j];a[m]++;}m++;if(t == 1) break;}if(t > 1) {ve.push_back(t);a[m]++, m++;}
}void dfs(int i, int now) //i是第几个质因子
{if(i == m){if(check(now)) ans++;return;}int t;_for(j, 0, a[i]){if(j == 0) t = 1;else t *= ve[i];if((long long)t * now > b1) break;dfs(i + 1, now * t);}
}int main()
{get_prime();int T;scanf("%d", &T);while(T--){scanf("%d%d%d%d", &a0, &a1, &b0, &b1);deal();ans = 0;dfs(0, 1);printf("%d\n", ans);}return 0;
}
X-factor Chain
我们拿100来看,100 = 2 x 2 x 5 x 5
那么很容易看出一个最长序列为
2 2 x 2 2 x 2 x 5 2 x 2 x 5 x 5
可以看出最长序列的长度为质因数的幂的和
那么一共有多少种呢?
上面的式子,我们可以只保留最后一个数
即2 2 5 5
这可以表示一种方案
我做的时候就卡在这里了,觉得可以用数学方法求出来,
但又不知道公式。
然后我就用搜索,然后超时。
后来看题解发现有公式。
有重复元素的排列方案:
设总元素个数为c,每个重复元素的数量为k1, k2, ……kn
那么答案就是c! / (k1! * k2!……kn!)
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;typedef unsigned long long ull;
ull p[25];int main()
{p[0] = 1;_for(i, 1, 21) p[i] = p[i-1] * i;int x; while(~scanf("%d", &x)){int a[30], m = 0;int t = x, sum = 0;memset(a, 0, sizeof(a));_for(i, 2, t / i)if(t % i == 0){while(t % i == 0) {t /= i;a[m]++;sum++;}m++;if(t == 1) break;}if(t > 1) a[m]++, m++, sum++;ull ans = p[sum];REP(i, 0, m)ans /= p[a[i]]; printf("%d %llu\n", sum, ans);}return 0;
}
[JLOI2014]聪明的燕姿
一开始就想到了公式,感觉可以搜索做,但不知道怎么个搜索方式。
想的是枚举n的因子,然后发现会很复杂。
但正解是反过来考虑,枚举质因子看n能不能被整除。
分两类。
(1)如果当前数很大,是一个质数与1的和就加入答案
(2)在根号当前数内枚举质数
#include<bits/stdc++.h>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;typedef long long ll;
const int MAXN = 1e5 + 10;
const int MOD = 1e9 + 7;
bool is_prime[MAXN];
vector<int> prime, ans;
int n;void get_prime()
{memset(is_prime, true, sizeof(is_prime));is_prime[0] = is_prime[1] = false;REP(i, 2, MAXN){if(is_prime[i]) prime.push_back(i);REP(j, 0, prime.size()){if(i * prime[j] >= MAXN) break;is_prime[i * prime[j]] = false;if(i % prime[j] == 0) break;}}
}bool check(int x) //如果数非常大就可以用这种方法判断质数
{if(x < MAXN) return is_prime[x];REP(j, 0, prime.size()){if((long long)prime[j] * prime[j] > x) return true;if(x % prime[j] == 0) return false;}
}void dfs(int k, int now, int num)
{if(num == 1) { ans.push_back(now); return; }if((k == -1 || (num - 1) > prime[k]) && check(num - 1)) ans.push_back(now * (num - 1));REP(j, k + 1, prime.size()){int p = prime[j];if(p * p > num) break;for(int sum = p + 1, t = p; sum <= num; t *= p, sum += t)if(num % sum == 0)dfs(j, now * t, num / sum);}
}int main()
{get_prime();while(~scanf("%d", &n)){ans.clear();dfs(-1, 1, n);printf("%d\n", ans.size());sort(ans.begin(), ans.end());REP(i, 0, ans.size()) {printf("%d", ans[i]);printf("%s", i == ans.size() - 1 ? "\n" : " ");}}return 0;
}