8,15模拟考试
sorce: 120
T1: 100
T2: 20
T3: 0
吐槽: 感觉 T2 和 T3 题目表述都不是太清楚,T3 考试结束也不知道题目啥意思/kk
T1 序列(sequence)
题目大意:
小 Z 有一个序列,定义 \(f(x)\) 为 \(x\) 在十进制下的位数,特别地,求 \(\sum_{1\leq i<j\leq n}f(a_i + a_j)\)
\(2 \leq n \leq 1000000\), $ 1\leq a_i \leq 10^8$
solution
\(n^2\) 暴力:枚举 \(i,j\) ,求个位数,最后求和就好。
\(O(n log n)\)
排序,一个数与比它小的数相加最多只可能进一位。
这样就可以 \(O(n)\) 扫一遍序列,每扫到一个数,就二分前面第一个能与它产生进位的数,然后就可以分两部分统计答案就好了。
code
/*
work by:Ariel_
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define int long long
#define rg register
using namespace std;
const int MAXN = 1e6 + 5;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9'){if (c == '-') f = -1;c = getchar();}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int n, min_digit, max_digit, pos, Ans, a[MAXN];
int get_digit(int x) {
if (x >= 1e8) return 9;
else if(x >= 1e7) return 8;
else if(x >= 1e6) return 7;
else if(x >= 1e5) return 6;
else if(x >= 1e4) return 5;
else if(x >= 1e3) return 4;
else if(x >= 100) return 3;
else if(x >= 10) return 2;
else return 1;
}
int get_L(int L, int R, int digit, int num) {
int ret = 0;
while(L <= R) {
int mid = (L + R) >> 1;
if (get_digit(a[mid] + num) >= digit) R = mid - 1, ret = mid;
else L = mid + 1;
}
return ret;
}
signed main(){
//freopen("sequence.in", "r", stdin);
//freopen("sequence.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
sort(a + 1, a + n + 1);
for (int i = 2; i <= n; i++) {
max_digit = get_digit(a[i] + a[i - 1]), min_digit = get_digit(a[i] + a[1]), pos = i - 1;
for (int j = max_digit; j >= min_digit; j--) {
Ans += (pos - get_L(1, pos, j, a[i]) + 1) * j;
pos = get_L(1, pos, j, a[i]) - 1;
}
}
printf("%lld", Ans);
puts("");
return 0;
}
T2 锁(lock)
直接看 题面 叭,实属概括不了题面。
关于 \(a_i = 1\) 的暴力。
摸样例,找规律,发现和组合数公式有关系
\(C_{n}^{m - 1}\) 从三十挂到了二十,组合数乘暴了,看别人代码发现,这样乘就不会爆。
\(30pts\)
ll C(int n, int m) {
ll ret1 = 1, ret2 = 1, ret3 = 1;
for (ll i = 1; i <= n; i++) ret1 *= i;
for (ll i = 1; i <= m; i++) ret2 *= i;
for (ll i = 1; i <= n - m; i++) ret3 *= i;
ret1 /= ret2 * ret3;
return ret1;
}
\(100pts\)
结论:重要程度之和小于 m,但加入任意一个居民都会导致重要度之和大于等于 m 的居民集合个数。
打算详细再证一下:
这样分组后的一组的重要度小于 \(m\) ,所以它们每一组都至少缺一把钥匙。
显然每一组加上其他任何一个人后重要值都会大于等于 \(m\) ,此时就要满足必须具有所有的钥匙,也就是其他人都必须有这一组缺少的所有钥匙。
这样分得的每两组都不同,所以每组对其他的人所要求的钥匙种类也不同。
反证法:假如 \(B\) 组有一个数 \(A\) 组之外如果这两组,并且 \(A, B\) 对其他人所要求的钥匙种类相同的话,那么 \(A\) 与 \(B\) 的要求在这个数上会发生矛盾。
这样推广到所有的组,如果每一组都只缺一种钥匙,那么总的需要的钥匙数 \(x\) ,因为要求钥匙数显然这样是最优的。
code
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
int n, m, a[21], maxnum[21], ans;
void dfs(int p, int left, int gaveup = inf) {
if (p > n || left <= maxnum[p]) {
if (gaveup >= left) ans++;
return;
}
if (left > a[p]) dfs(p + 1, left - a[p], gaveup);
dfs(p + 1, left, min(gaveup, a[p]));
}
int main(){
//freopen("lock.in", "r", stdin);
//freopen("lock.out", "w", stdout);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
maxnum[n] = a[n];
for (int i = n - 1; i >= 1; i--) maxnum[i] = min(maxnum[i + 1], a[i]);
dfs(1, m);
cout << ans << endl;
return 0;
}
T3 square
小 \(Z\) 现在有规格为 \(a\times b\) \((1 \leq a,b \leq n)\) 的地板,她只能用这一种规格的地板来拼成正方形(边长任意,不能旋转),为了省钱,她会购买尽可能数量少的地板。
对于每一对 \(a,b\) 你要求出她最少需要购买的地板数量。
由于输出可能很大,所以你只需要输出所有答案的乘积即可,为了避免高精度,小 \(Z\) 很良心的让你将答案对 \(19260817\) 取模。
solution
读懂题目之后,很容易列出这么个式子(\(a, b\) 都用 \(i ,j\) 表示了):
\(\prod_{i, j = 1}^{n}\frac{lcm(i,j)}{i}\times \frac{lcm(i,j)}{j}\)
由 \(lcm(i, j) = \frac{i,j}{gcd(i,j)}\) 化简得到:
\(\prod_{i = 1}^{n}\prod_{j = 1}^{n} \frac {ij}{gcd(i,j)^2}\) 这不就是这道题么 P5221 Product
先看上面 \(\prod_{i = 1}^{n}\prod_{j = 1}^{n} ij\)
\(\prod_{j = 1}^{n} ij = 1\times i\times2\times i\dots n\times i\)
于是可以得到 \(\prod_{i=1}^{n} i^nn! = (\prod_{i = 1}^{n}i^n)\times (n!)^n = (n!)^n\times (n!)^n = (n!)^{2n}\)
再看下面 \(\prod_{i = 1}^{n}\prod_{j = 1}^{n} gcd(i, j)^{-2}\)
先不看 -2 次方。
系数就是个欧拉函数常见的应用,预处理出欧拉函数的前缀和就可以 \(O(1)\) 算出指数了。
\(sum[x] = \sum_{i=1}^{x} \phi(i)\)
然后原式就为
\((n!)^{2n}\times (\prod_{d = 1}^{n} d^{2\times sum[\frac{n}{d}] - 1})^{-2}\)
然后就可以 \(O(nlogn)\) 求答案了。
欧拉函数的前缀和会爆 int,但用long long 会 MLE,根据欧拉定理,可以直接再质数上取模,又因为 mod 是个质数,所以直接对 mod - 1 取模就好。
\((n!)^{2n}\times (\prod_{d = 1}^{n} d^{(2 \times sum[\frac{n}{d}] - 1)\%(mod - 1)})^{-2}\)
这样就不需要开 long long 了。