目录
1. 给出 n n n,问满足 1 x + 1 y = 1 n \cfrac{1}{x}+\cfrac{1}{y}=\cfrac{1}{n} x1+y1=n1 的解有多少种?
令
x
=
n
+
a
x=n+a
x=n+a,
y
=
n
+
b
y=n+b
y=n+b,则
1
x
+
1
y
=
1
n
\cfrac{1}{x}+\cfrac{1}{y}=\cfrac{1}{n}
x1+y1=n1
1 n + a + 1 n + b = 1 n \cfrac{1}{n+a}+\cfrac{1}{n+b}=\cfrac{1}{n} n+a1+n+b1=n1
2 n + a + b n 2 + ( a + b ) n + a b = 1 n \cfrac{2n+a+b}{n^2+(a+b)n+ab}=\cfrac{1}{n} n2+(a+b)n+ab2n+a+b=n1
2 n 2 + ( a + b ) n = n 2 + ( a + b ) n + a b 2n^2+(a+b)n=n^2+(a+b)n+ab 2n2+(a+b)n=n2+(a+b)n+ab
∴ n 2 = a b \therefore n^2=ab ∴n2=ab
通过唯一基本定理: n = p 1 k 1 × p 2 k 2 × . . . . × p m k m n=p_1^{k_1}\times p_2^{k_2}\times ....\times p_m^{k_m} n=p1k1×p2k2×....×pmkm
∴ n 2 = p 1 2 k 1 × p 2 2 k 2 × . . . . × p m 2 k m \therefore n^2=p_1^{2k_1}\times p_2^{2k_2}\times ....\times p_m^{2k_m} ∴n2=p12k1×p22k2×....×pm2km
∴ a n s = ( 2 k 1 + 1 ) ( 2 k 2 + 1 ) . . . ( 2 k m + 1 ) \therefore ans=(2k_1+1)(2k_2+1)...(2k_m+1) ∴ans=(2k1+1)(2k2+1)...(2km+1)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n, vis[N];
vector<int> prim;
void getPrime(int n) {
vis[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) {
prim.push_back(i);
}
for (int p:prim) {
if (p * i > n) break;
vis[p * i] = 1;
if (i % p == 0) break;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
getPrime(10000);
int T;
cin >> T;
for (int cs = 1; cs <= T; cs++) {
cin >> n;
ll res = 1;
for (int p : prim) {
if (n % p == 0) {
int cnt = 0;
while (n % p == 0) {
cnt++;
n /= p;
}
res *= (2 * cnt + 1);
}
if (p * p > n) break;
}
if (n > 1) res *= 3;//2*1+1
cout << res << endl;
}
return 0;
}
2. 整数划分
题目
将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,其中n1≥n2≥…≥nk≥1,k≥1。
正整数n的这种表示称为正整数n的划分。求正整数n的不同划分个数。
例如正整数6有如下11种不同的划分:
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1。
题解 - 整数划分问题
设 f ( n , m ) f(n,m) f(n,m) 表示当 n = ∑ m i n=\sum m_i n=∑mi 且 m = max ( m i ) m=\max(m_i) m=max(mi)时的整数 n n n 的不同划分个数
#include <bits/stdc++.h>
using namespace std;
int n;
int dfs(int n, int m) {
if (m == 1) return 1; // 只有 {1,1,..,1}
else if (n < m) return dfs(n,n); // 不可能存在符合的集合 缩小m的范围
else if (n == m)
return dfs(n, m - 1) // 拆开最大值 m 至少从m中拆出一个1
+ 1; // 存在 {n}
else
return dfs(n - m, m) // 符合的集合里含有最大值 m
+ dfs(n, m - 1); // 拆开最大值 m 至少从m中拆出一个1
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
while (cin >> n) {
cout << dfs(n, n) << endl;
}
return 0;
}
3. 数的划分
题目
将整数n分成k份,且每份不能为空,任意两种分法不能相同(不考虑顺序)。例如:n=7,k=3,下面三种分法被认为是相同的。
1,1,5;1,5,1;5,1,1;
问有多少种不同的分法。
设 f ( n , m ) f(n,m) f(n,m) 表示整数 n n n 分成 m m m 份在不考虑顺序下的不同分法
f ( n , m ) = f ( n − 1 , m − 1 ) + f ( n − m , m ) f(n,m)=f(n-1,m-1)+f(n-m,m) f(n,m)=f(n−1,m−1)+f(n−m,m)
- 第 n n n 份单独成一组
- 将第 n n n 份插入到前面 m m m 组里中的
第二种的解释 - https://blog.csdn.net/elma_tww/article/details/86538836?utm_source=app
ps:
设 f ( n , m ) f(n,m) f(n,m) 表示整数 n n n 分成 m m m 份在考虑顺序下的不同分法
f ( n , m ) = f ( n − 1 , m − 1 ) + f ( n − 1 , m ) f(n,m)=f(n-1,m-1)+f(n-1,m) f(n,m)=f(n−1,m−1)+f(n−1,m)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
int n, m, k;
int f[N][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
while (cin >> n >> k) {
f[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
if (i < j)
f[i][j] = 0;
else
f[i][j] = f[i - 1][j - 1] // 第i份单独成一组
+ f[i - j][j]; // 将第i份插入到前面j组里的
}
}
cout << f[n][k] << endl;
}
return 0;
}
4. 哈夫曼编码的一个实际应用 / 哈夫曼编码的一个实际应用(压缩)
坑点:吃回车问题
#include <bits/stdc++.h>
using namespace std;
#define between(x, a, b) (a<=x && x<=b)
int n, dfn = 0;
struct Node {
int val, priority;// 权值 优先级
char c;// 字符 (只对叶子节点有意义)
Node *l, *r;
Node(int x, char y) {
priority = ++dfn;
c = y, val = x;
l = r = nullptr;
}
Node(Node *x, Node *y) {
priority = ++dfn;
val = 0;
if (x) {
val += x->val;
l = x;
}
if (y) {
val += y->val;
r = y;
}
}
};
struct cmp {
bool operator()(Node *a, Node *b) {
if (a->val == b->val) {
return a->priority > b->priority;
}
return a->val > b->val;
}
};
struct Huffman {
Node *rt;
priority_queue<Node *, vector<Node *>, cmp> q; // 优先队列
void build(vector<Node *> v) {
for (auto u:v) {
q.push(u);
}
while (q.size() >= 2) {
auto x = q.top();
q.pop();
auto y = q.top();
q.pop();
q.push(new Node(x, y));
}
rt = q.top();
q.pop();
}
// 解码 二进制解码
int pos = 0, flag;
// 在哈夫曼树上从根节点一直爬到叶子节点
void dfs(Node *u, vector<int> &bit) {
if (!u->l && !u->r) {
cout << u->c;
return;
}
if (pos >= bit.size()) { // 下标越界 不存在字符
flag = 0;
return;
}
if (!bit[pos++]) {//0
if (u->l) dfs(u->l, bit);
else flag = 0;
} else {
if (u->r) dfs(u->r, bit);
else flag = 0;
}
}
void decode(vector<int> bit) {
flag = 1, pos = 0; // flag 错误标志 pos 当前读到的二进制的位置
int n = bit.size();
while (pos < n && flag) {
dfs(rt, bit);
}
if (!flag) cout << "||Error !";
cout << endl;
}
// 编码 根据文本返回二进制编码 (以字符串的形式)
map<char, string> mp;
void getMapNode() {
mapDfs(rt, "");
}
void mapDfs(Node *u, string s) {
if (!u->l && !u->r) {
mp[u->c] = s;
return;
}
if (u->l) mapDfs(u->l, s + "0");
if (u->r) mapDfs(u->r, s + "1");
}
string code(string s) {
string res = "";
for (char c:s) {
res += mp[c];
}
while (res.size() % 8) res += "0";
return res;
}
};
int bit[5];
// 16进制转2进制 - 将十六进制的每个数都拆成4位二进制
vector<int> toBinary(string s) {
vector<int> res;
int x;
for (char c:s) {
if (between(c, '0', '9')) x = c - '0';
else x = c - 'A' + 10;
fill(bit, bit + 5, 0);
int i = 0;
while (x) {
bit[++i] = x % 2;
x /= 2;
}
i = 4;
while (i) res.push_back(bit[i--]);
}
return res;
}
// 2进制转16进制
string toHex(string s) {
string res = "";
int n = s.length();
for (int i = 0; i < n; i += 4) {
int x = 0;
for (int j = 0; j < 4; ++j) {
x = x * 2 + s[i + j] - '0';
}
if (between(x, 0, 9)) res += (char) (x + (int) '0');
else res += (char) (x - 10 + (int) 'A');
}
return res;
}
string s;
void Solve_Code() {
dfn = 0;//时间序
getline(cin, s);
n = s.length();
vector<Node *> v;// 结点指针集合
for (int i = 0, x; i < n; ++i) {
cin >> x;
v.push_back(new Node(x, s[i]));
}
Huffman huffman;
huffman.build(v);//构建哈夫曼树
huffman.getMapNode();//获得每个叶子节点的二进制编码值
cin >> n;
getline(cin, s);//吃回车
for (int i = 1; i <= n; ++i) {
getline(cin, s);
cout << toHex(huffman.code(s)) << endl;
}
}
void Solve_Decode() {
dfn = 0;//时间序
getline(cin, s);
n = s.length();
vector<Node *> v;// 结点指针集合
for (int i = 0, x; i < n; i++) {
cin >> x;
v.push_back(new Node(x, s[i]));
}
Huffman huffman;
huffman.build(v);//构建哈夫曼树
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> s;
huffman.decode(toBinary(s));//先将16进制转化为2进制 再拿二进制去爬哈夫曼树
}
}
void Run() {
int T;
cin >> T;
getline(cin, s);//吃回车
for (int cs = 1; cs <= T; cs++) {
Solve_Code();
//Solve_Decode(); // 解码时 前面吃回车的getline的位置要改到for里面来
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
Run();
return 0;
}
5. 1x1、2x2、3x3、4x4、5x5、6x6盒子打包问题
#include <bits/stdc++.h>
using namespace std;
int n, m, k;
int cnt[20];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
while (true) {
n = 0;
for (int i = 1; i <= 6; i++) {
cin >> cnt[i];
n += cnt[i];
}
if (n == 0) break;
int res = 0;
int num1 = 0, num2 = 0, tmp;//留给1x1 2x2的剩余位置个数
res += cnt[6];// 6x6 5x5 4x4 的盒子一定是单独放一个盒子的
res += cnt[5];
res += cnt[4];
num2 += cnt[4] * 5;// 4x4剩余部分可以放5个2x2
res += cnt[3] / 4;
tmp = cnt[3] % 4;
if (tmp) { // 根据3x3剩余的位置 判断可以给2x2留下最多多少个位置
res++;
if (tmp == 1) num2 += 5; //注意这里
else if (tmp == 2) num2 += 3;
else num2 += 1;
}
if (cnt[2] > num2) {
tmp = cnt[2] - num2;
res += tmp / 9; // 单独装 2x2
tmp %= 9;
if (tmp) res++;
}
num1 = res * 36 - cnt[6] * 36 - cnt[5] * 25 - cnt[4] * 16 - cnt[3] * 9 - cnt[2] * 4;
if (cnt[1] > num1) {
tmp = cnt[1] - num1;
res += tmp / 36;
tmp %= 36;
if (tmp) res++;
}
cout << res << endl;
}
return 0;
}
6. 基站安装
#include <bits/stdc++.h>
using namespace std;
int n, m, k;
double d;
struct Seg {
double S, T;
bool operator<(const Seg oth) const {
return S < oth.S;
}
} seg[1005];
struct Point {
double x, y;
void read() {
cin >> x >> y;
}
bool isLegal() {
return !(y > d);
}
bool operator<(const Point oth) const {
if (x == oth.x) return y > oth.y;
return x < oth.x;
}
Seg getSeg() {
return {
x - sqrt(d * d - y * y),
x + sqrt(d * d - y * y)
};
}
} p[1005];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int cs = 0;
while (cin >> n >> d) {
if (n == 0 && d == 0) {
break;
}
int flag = 0;
for (int i = 1; i <= n; i++) {
p[i].read();
if (!p[i].isLegal()) {
flag = 1;
}
}
cout << "Case " << ++cs << ": " ;
if (flag) {
cout << -1 << endl;
} else {
for (int i = 1; i <= n; i++) {
seg[i] = p[i].getSeg();
}
sort(seg + 1, seg + 1 + n);
double l = seg[1].S, r = seg[1].T;
int res = 1;
for (int i = 2; i <= n; i++) {
l = max(l, seg[i].S);
r = min(r, seg[i].T);
if (l > r) {
l = seg[i].S;
r = seg[i].T;
res++;
}
}
cout << res << endl;
}
}
return 0;
}
7. 军训选人问题
n个人,给出每个人的身高,找到最长连续区间,使得区间的最大值与最小值的差值小于d
n ≤ 1 e 5 , 140 ≤ h ≤ 250 n\le1e5,140\le h\le 250 n≤1e5,140≤h≤250
坑点:
1.RMQ爆空间
2.数据太水了,n2暴力都给过了,数据要是严一点分分钟卡掉(lll¬ω¬)
正解:
multiset(一个优化的二叉树,下面简称mst)+双指针
将区间内的所有元素都放到mst里,
区间最小值就是二叉树上最左边的点:*mst.begin()
区间最大值就是树上最右边的点:*(--mst.end())
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, d;
multiset<int> mst;
int a[N];
void Solve() {
while (cin >> n >> d) {
mst.clear();
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int S = 0, T = 0;
for (int r = 1, l = 1; r <= n; r++) {
mst.insert(a[r]);
while (l < r && *(--mst.end()) - *mst.begin() > d) {
mst.erase(mst.find(a[l++])); // mst里删除啊a[l]、
// 注意:不能写成mst.erase(a[l]) 会将所有与a[l]相同的点都删除的
}
if (T - S + 1 < r - l + 1) {
S = l, T = r;
}
}
cout << "From=" << S << ",To=" << T << endl;
cout << "MaxLen=" << T - S + 1 << endl;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
Solve();
return 0;
}
8. 军训选人问题(续)
n个人取长度为m的连续区间,问区间内最大值-最小值的差值的最小值
滑动区间+multiset,这个就比上面简单了
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, m;
multiset<int> mst;
void Solve() {
while (cin >> n >> m) {
mst.clear();
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
int S = 0, T = 0, diff = 0x3f3f3f3f;
for (int r = 1, l = 1; r <= n; ++r) {
mst.insert(a[r]);
while (l < r && r - l + 1 > m) {
auto it = mst.find(a[l]);
mst.erase(it);
l++;
}
if (r - l + 1 == m) {
int x = *(--mst.end()) - *mst.begin();
if (x < diff) {
diff = x;
S = l, T = r;
}
}
}
if (n >= m) {
cout << "From=" << S << ",To=" << T << endl;
cout << "MinDiff=" << diff << endl;
} else {
cout << "No solution !" << endl;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
Solve();
return 0;
}
9. 最长公共子序列(输出公共序列)
输出路径就是在二维矩阵上从坐标n,m按照状态转移往回爬
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
const int N = 1e3 + 10;
int n, m, k;
string A, B;
pii pre[N][N];
void out(int i, int j) {
if (!i || !j) return;
out(pre[i][j].first, pre[i][j].second);
if (A[i] == B[j]) {
cout << A[i];
}
}
void Solve() {
cin >> A >> B;
n = A.length(), m = B.length();
A = " " + A, B = " " + B;
vector<vector<int>> f(n + 2, vector<int>(m + 1, 0));
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (A[i] == B[j]) {// 如果相等 一定会增大 f一定最优
f[i][j] = f[i - 1][j - 1] + 1;
pre[i][j] = {i - 1, j - 1};
}else if (f[i - 1][j] >= f[i][j - 1]) {
f[i][j] = f[i - 1][j];
pre[i][j] = {i - 1, j};
} else {
f[i][j] = f[i][j - 1];
pre[i][j] = {i, j - 1};
}
}
}
cout << f[n][m] << endl;
out(n, m);
cout << endl;
}
void Run() {
int T;
cin >> T;
for (int cs = 1; cs <= T; cs++) {
//cout << "Case " << cs << ": ";//printf("Case %d: ", cs);
Solve();
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
Run();
return 0;
}