Loading

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 次方。

\[\prod_{i = 1}^{n}\prod_{j = 1}^{n} gcd(i, j)\\ = \prod_{d = 1}^{n} \prod_{i = 1}^{n}\prod_{j = 1}^{n} d[gcd(i, j) == d]\\ = \prod_{d = 1}^{n} d^{\sum_{i = 1}^{n} \sum_{j = 1}^{n}[gcd(i, j) == d]}\\ = \prod_{d = 1}^{n} d^{\sum_{i = 1}^{\frac {n}{d}} \sum_{j = 1}^{\frac {n}{d}}[gcd(i, j) == 1]}\\ \]

系数就是个欧拉函数常见的应用,预处理出欧拉函数的前缀和就可以 \(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 了。

posted @ 2021-08-16 10:09  Dita  阅读(55)  评论(1编辑  收藏  举报