• 8,15模拟考试


    sorce: 120

    T1: 100

    T2: 20

    T3: 0

    吐槽: 感觉 T2 和 T3 题目表述都不是太清楚,T3 考试结束也不知道题目啥意思/kk

    T1 序列(sequence)

    题目大意:

    小 Z 有一个序列,定义 (f(x))(x) 在十进制下的位数,特别地,求 (sum_{1leq i<jleq n}f(a_i + a_j))

    (2 leq n leq 1000000), $ 1leq 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 imes 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} imes 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 imes i imes2 imes idots n imes i)

    于是可以得到 (prod_{i=1}^{n} i^nn! = (prod_{i = 1}^{n}i^n) imes (n!)^n = (n!)^n imes (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} imes (prod_{d = 1}^{n} d^{2 imes sum[frac{n}{d}] - 1})^{-2})

    然后就可以 (O(nlogn)) 求答案了。

    欧拉函数的前缀和会爆 int,但用long long 会 MLE,根据欧拉定理,可以直接再质数上取模,又因为 mod 是个质数,所以直接对 mod - 1 取模就好。

    ((n!)^{2n} imes (prod_{d = 1}^{n} d^{(2 imes sum[frac{n}{d}] - 1)\%(mod - 1)})^{-2})

    这样就不需要开 long long 了。

  • 相关阅读:
    Oracle自带的sql developer导入导出数据
    LSMW批处理工具操作手册
    JS控制TABLE表格在任意一行下面添加一行(有待完善)
    SAP销售模块塑工常见问题和解决方案(自己收藏)
    SAP采购订单历史明细报表源代码(自己收藏)
    SAP公司间采购订单关联交货单报表源代码(自己收藏)
    《嵌入式系统可靠性设计技术及案例解析》读书笔记(七)
    《嵌入式系统可靠性设计技术及案例解析》读书笔记(六)
    《嵌入式系统可靠性设计技术及案例解析》读书笔记(五)
    《嵌入式系统可靠性设计技术及案例解析》读书笔记(四)
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15146404.html
Copyright © 2020-2023  润新知