九月题目泛做总结。

Keywords

Mathematics, Floyd, Transitive Closure, Bitset, Light-Heavy Decomposition

Table of Contents

  1. Keywords
  2. BZOJ 2257 瓶子和燃料
    1. Description
    2. Proof
    3. Solution
  • BZOJ 3629 聪明的燕姿
    1. Description
    2. Proof
    3. Solution
  • BZOJ 1703 奶牛排名
    1. Description
    2. Solution
  • Luogu P3398 仓鼠找Sugar
    1. Solution
  • 数学是需要加强的地方, Bouvardia不要鸟其他人.

    BZOJ 2257 瓶子和燃料

    Description

    给一些有容量的瓶子, 可以进行如下操作:

    装满; 倒空; 从一瓶向另一瓶, 直到其中一个空或一个满.

    最大化这些操作能得到的最小正体积.

    Proof

    todo…

    Solution

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cmath>

    using namespace std;

    const int maxn = 1005;

    int n, k, v[maxn], lst[maxn * maxn], ptr;

    void divide(int x)
    {
    int lim = sqrt(x);
    for(int i = 1; i <= lim; ++i)
    if(!(x % i))
    lst[++ptr] = i, lst[++ptr] = x / i;
    if(lim * lim == x) ptr--;
    }
    bool cmp(int a, int b)
    {
    return a > b;
    }

    int main()
    {
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; ++i) scanf("%d", &v[i]);
    for(int i = 1; i <= n; ++i)
    divide(v[i]);
    sort(lst + 1, lst + 1 + ptr, cmp);
    int tmp = 1;
    for(int i = 1; i <= ptr; ++i)
    {
    if(lst[i] == lst[i - 1]) tmp++;
    else tmp = 1;
    if(tmp >= k)
    {
    printf("%d\n", lst[i]);
    return 0;
    }
    }
    return 0;
    }

    BZOJ 3629 聪明的燕姿

    Description

    统计约数和为 $S$ 的正整数个数.

    Proof

    todo…

    Solution

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <vector>
    #include <bitset>
    #include <vector>

    using namespace std;

    typedef long long ll;

    const int maxn = 1e+5 + 5;

    int s, ans, lim;

    bitset<maxn> ispr; int prime[maxn >> 1], cnt;
    inline void euler()
    {
    ispr.set(); ispr.reset(1);
    for(int i = 2; i < maxn; ++i)
    {
    if(ispr.test(i)) prime[++cnt] = i;
    for(int j = 1; j <= cnt && i * prime[j] < maxn; ++j)
    {
    ispr.reset(i * prime[j]);
    if(!(i % prime[j])) break;
    }
    }
    }
    inline int isprime(int x)
    {
    if(x < maxn) return ispr.test(x);
    for(int i = 1; i <= cnt && prime[i] * prime[i] <= x; ++i)
    if(!(x % prime[i])) return 0;
    return 1;
    }

    vector<int> vec;
    inline void dfs(ll del, int id, ll prod)
    {
    if(del == 1)
    {
    vec.push_back(prod);
    return;
    }
    if(del - 1 >= prime[id] && isprime(del - 1))
    vec.push_back(prod * (del - 1));
    ll prd, sum;
    for(int i = id; prime[i] * prime[i] <= del; ++i)
    {
    prd = prime[i];
    sum = prime[i] + 1;
    for(; sum <= del; prd *= prime[i], sum += prd)
    if(!(del % sum)) dfs(del / sum, i + 1, prod * prd);
    }
    return;
    }

    int main()
    {
    euler();
    while(scanf("%d", &s) != EOF)
    {
    vec.clear();
    dfs((ll)s, 1, 1ll);
    printf("%lu\n", vec.size());
    sort(vec.begin(), vec.end());
    for(vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)
    printf("%d ", (*i));
    if(vec.size()) putchar('\n');
    }
    return 0;
    }

    BZOJ 1703 奶牛排名

    Description

    已知一些大小关系, 求最少还要测量多少对大小关系, 可以保证能够得出所有对象两两间的大小关系.

    Solution

    模型转有向图, 不知道的就是两两之间互不可达的点.

    跑一遍传递闭包, 剩下的统计答案.

    但是! 跑不下来. 所以要Bitset优化.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <bitset>

    using namespace std;

    const int maxn = 1005;

    int n, m;
    bitset<maxn> mtr[maxn];

    int main()
    {
    scanf("%d%d", &n, &m);
    int x, y;
    for(int i = 1; i <= m; ++i)
    {
    scanf("%d%d", &x, &y);
    mtr[x][y] = 1;
    }
    for(int k = 1; k <= n; ++k)
    for(int i = 1; i <= n; ++i)
    if(mtr[i][k])
    mtr[i] |= mtr[k];
    int tot = 0;
    for(int i = 1; i <= n; ++i)
    for(int j = i + 1; j <= n; ++j)
    if(!mtr[i][j] && !mtr[j][i]) tot++;
    printf("%d\n", tot);
    return 0;
    }

    Luogu P3398 仓鼠找Sugar

    Solution

    这题比较裸, 本来不是很想写. 但是一个Trick感觉会有用.

    链剖每次路径加再删除会慢, 所以每次打一个时间戳, 查区间最大值是否符合当前时间戳.

    对于不好每次都修改再撤回的记录, 可以按时间戳递增地打标记, 每次取用时, 只要看是不是等于当前时间戳就好. 这样的思想在另一道题里也有体现, 会再提到.