九月题目泛做总结。

maki美如画中仙!

Keywords

Binary Indexed Tree, Matrix Multiplication, Mathematics, Matrix Hash, Memorization&Search, KMP

Table of Contents

  1. Keywords
  2. BZOJ 2743 采花
    1. Description
    2. Solution
  • BZOJ 2326 数学作业
    1. Description
    2. Solution
  • BZOJ 2721 [Violet 5]樱花
    1. Description
    2. Solution
  • BZOJ 1567 Blue Mary的战略地图
    1. Description
    2. Solution
  • BZOJ 1079 着色方案
    1. Description
    2. Solution
  • BZOJ 1511 OKR
    1. Description
    2. Solution
  • BZOJ 2743 采花

    Description

    与HH的项链不同的是, 这次要统计至少出现2次之类的数量.

    能够成功采花的条件是, 之前采过同样的花, 在区间内有可预见的同种花.

    求颜色种类数.

    Solution

    同样记录向后第一个同色出现位置, 对答案排序, 离线作答.

    枚举左端点, 每当我们要离开一个位置时, 由于她的下一个位置不再有保证, 所以树状数组中加入 $-1$. 她下一个位置的下一个位置此时有了保障, 树状数组中加入 $1$.

    求一个区间和就是答案.

    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
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cctype>

    using namespace std;

    const int maxn = 2e+6 + 5;

    struct node
    {
    int l, r, id;
    bool operator < (const node &rhs) const
    {
    return l < rhs.l;
    }
    }t[maxn];

    int n, m, c, a[maxn], lst[maxn], nxt[maxn];
    int b[maxn], ans[maxn];

    inline void add(int x, int v){for(; x <= n; x += (x & -x)) b[x] += v;}
    inline int query(int x){int ret = 0; for(;x;x-=(x&-x))ret+=b[x]; return ret;}

    inline int rd()
    {
    register int x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    return x;
    }

    int main()
    {
    n = rd(); c = rd(); m = rd();
    for(int i = 1; i <= n; ++i) a[i] = rd();
    for(int i = n; i; --i)
    nxt[i] = lst[a[i]], lst[a[i]] = i;
    for(int i = 1; i <= c; ++i)
    if(nxt[lst[i]]) add(nxt[lst[i]], 1);
    for(int i = 1; i <= m; ++i)
    t[i].l = rd(), t[i].r = rd(), t[i].id = i;
    sort(t + 1, t + 1 + m);
    int ptr = 1;
    for(int i = 1; i <= m; ++i)
    {
    while(ptr < t[i].l)
    {
    if(nxt[ptr]) add(nxt[ptr], -1);
    if(nxt[nxt[ptr]]) add(nxt[nxt[ptr]], 1);
    ptr++;
    }
    ans[t[i].id] = query(t[i].r) - query(t[i].l - 1);
    }
    for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
    return 0;
    }

    BZOJ 2326 数学作业

    Description

    定义一个操作 $Concatenate(1\ldots N)\; Mod \; M = \overline{1234\ldots N}\;Mod\;M$.

    $1 \le N \le 10^{18}, \, 1\le M \le 10^9$.

    Solution

    $N$ 太大了. 一看过去是矩乘, 但是怎么转移呢?

    考虑分段在每个长度下转移, 由于每次增加的长度固定, 就比较好想了, 而且额外的复杂度不多. 套了个$lg$.

    转移就是:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    $
    \begin{pmatrix}
    f[n] & n & 1
    \end{pmatrix}
    =
    \begin{pamtrix}
    f[n-1] & n-1 & 1
    \end{pmatrix}
    \begin{pmatrix}
    10^k & 0 & 0\\
    1 & 1 & 0\\
    1 & 1 & 0\\
    \end{pmatrix}
    $

    枚举位数, 一直做到 $N$ 就好了.

    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
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>

    using namespace std;

    typedef unsigned long long ll;

    ll n; ll mod;
    inline ll quick_mul(ll base, ll index)
    {
    ll ret = 0;
    while(index)
    {
    if(index & 1) ret = (ret + base) % mod;
    index >>= 1;
    base = (base + base) % mod;
    }
    return ret;
    }
    struct matrix
    {
    ll a[5][5]; int N, M;
    matrix(){memset(a, 0, sizeof a);}
    ll& operator () (int x, int y)
    {
    return a[x][y];
    }
    matrix operator * (matrix rhs)
    {
    matrix ret; ret.N = N; ret.M = rhs.M;
    for(int k = 1; k <= M; ++k)
    for(int i = 1; i <= N; ++i)
    for(int j = 1; j <= rhs.M; ++j)
    ret(i,j) = (ret(i, j) + quick_mul(a[i][k], rhs(k, j))) % mod;
    return ret;
    }
    }ans, mtr;
    inline void quick_power(matrix &res, matrix base, ll index)
    {
    while(index)
    {
    if(index & 1) res = res * base;
    index >>= 1;
    base = base * base;
    }
    }
    inline void solve(ll k, ll term)
    {
    mtr = matrix(); mtr.N = mtr.M = 3;
    ll index = term - k/10 + 1;
    mtr(1,1) = k;
    mtr(2,1) = mtr(2,2) = mtr(3,1) = mtr(3,2) = mtr(3,3) = 1;
    quick_power(ans, mtr, index);
    }

    int main()
    {
    scanf("%llu%llu", &n, &mod);
    ans.N = 1; ans.M = 3; ans(1,3) = 1;
    ll t = 10;
    while(t <= n)
    solve(t, t - 1), t *= 10;
    solve(t, n);
    printf("%llu\n", ans(1,1));
    return 0;
    }

    BZOJ 2721 [Violet 5]樱花

    Description

    求满足 $\frac{1}{x} + \frac{1}{y} = \frac{1}{N!} $ 的正整数解组数.

    Solution

    推式子什么的, Bouvardia很需要呢.

    $yN! + xN! = xy,$

    $xy - (x+y)N! = 0,$

    $xy - (x+y)N! + {N!}^2 = {N!}^2,$

    $(x - N!)(y - N!) = N!^2.$

    为了使结果是正整数, $x,\, y$ 也一定是正整数, 保证合法.

    那么就转化成求有多少组$\,A,\,B$, 满足$AB = N!^2$ 的问题了.

    考虑求出素因子指数, 这时只要确定$A$的一个指数序列, $B$也就能对应求出, 乘法原理就可以求出解的组数.

    筛出素数, $N!$ 有一个这样的性质:

    对于一个数 $k$, 一定有 $N!$ 中 $k$ 的指数 $\begin{align}q = \sum_{i = 0}^{\infty}\lfloor \frac{N}{k^i} \rfloor \end{align}$.

    这样就可以求了, 因为是平方, 记得对求出的指数乘个2.

    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
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <bitset>

    using namespace std;

    typedef long long ll;

    const int maxn = 1e+6 + 5;
    const int maxp = 4e+5 + 5;
    const int p = 1e+9 + 7;

    bitset<maxn> ispr;
    int n, pr[maxp], ind[maxp], mindiv[maxn], cnt;

    inline void euler()
    {
    ispr.set();
    for(int i = 2; i < maxn; ++i)
    {
    if(ispr[i]) pr[++cnt] = i;// mindiv[i] = cnt;
    for(int j = 1; j <= cnt && i * pr[j] < maxn; ++j)
    {
    ispr.reset(i * pr[j]);
    //mindiv[i * pr[j]] = j;
    if(!(i % pr[j])) break;
    }
    }
    }

    int main()
    {
    euler();
    scanf("%d", &n);
    for(int i = 1; i <= cnt && pr[i] <= n; ++i)
    for(ll t = pr[i]; t <= (ll)n; t *= (ll)pr[i])
    ind[i] += n/t;
    //puts("sta");
    for(int i = 1; i <= cnt && pr[i] <= n; ++i) ind[i] *= 2;
    ll ans = 1;
    for(int i = 1; i <= cnt && pr[i] <= n; ++i)
    ans = (ans * (ind[i] + 1)) % p;
    printf("%lld\n", ans);
    return 0;
    }

    BZOJ 1567 Blue Mary的战略地图

    Description

    求两个矩阵中最大的完全相同的正方形子矩阵.

    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
    75
    76
    77
    78
    79
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>

    using namespace std;

    typedef unsigned long long ll;

    const int maxn = 55;
    const int hsh = 2879;
    const int hsh2 = 6277;

    int n, a[55][55], b[55][55];
    ll fa[55][55], fb[55][55], R[55], arr[2505];

    ll col(int t_, int x, int y, int l)
    {
    ll ret = 1;
    for(int i = x; i <= x + l - 1; ++i)
    for(int j = y; j <= y + l - 1; ++j)
    {
    if(t_)
    ret = ret * hsh2 + fa[i][y + l - 1] - fa[i][y - 1]*R[l];
    else
    ret = ret * hsh2 + fb[i][y + l - 1] - fb[i][y - 1]*R[l];
    }
    return ret;
    }
    bool chk(int mid)
    {
    int tot = 0; ll cur;
    for(int i = 1; i <= n - mid + 1; ++i)
    for(int j = 1; j <= n - mid + 1; ++j)
    arr[++tot] = col(0, i, j, mid);
    sort(arr + 1, arr + 1 + tot);
    for(int i = 1; i <= n - mid + 1; ++i)
    for(int j = 1; j <= n - mid + 1; ++j)
    {
    cur = col(1, i, j, mid);
    if(*lower_bound(arr + 1, arr + 1 + tot, cur) == cur)
    return true;
    }
    return false;
    }

    int main()
    {
    R[0] = 1;
    for(int i = 1; i <= 50; ++i) R[i] = R[i - 1] * hsh;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= n; ++j)
    scanf("%d", &a[i][j]);
    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= n; ++j)
    scanf("%d", &b[i][j]);
    for(int i = 1; i <= n; ++i)
    {
    fa[i][0] = 1;
    for(int j = 1; j <= n; ++j)
    fa[i][j] = fa[i][j - 1] * hsh + a[i][j];
    }
    for(int i = 1; i <= n; ++i)
    {
    fb[i][0] = 1;
    for(int j = 1; j <= n; ++j)
    fb[i][j] = fb[i][j - 1] * hsh + b[i][j];
    }
    int l = 1, r = n, ans;
    while(l <= r)
    {
    int mid = (l + r) >> 1;
    if(chk(mid)) ans = mid, l = mid + 1;
    else r = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
    }

    BZOJ 1079 着色方案

    Description

    每种颜色可以涂若干格, 求相邻格子颜色各不相同的方案数.

    Solution

    这题好仙的说……考虑到在一个状态下, 剩余量相同的颜色本质是相同的, 因为在合法情况下, 它们一定可以互换. 所以只要记录各种余量的颜色种类数就好了.

    一开始想map动态开状态, 后来又想容斥. Bouvardia还是需要对题目做出准确而简洁的反应呐.

    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
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>

    using namespace std;

    typedef long long ll;

    const int mod = 1e+9 + 7;

    ll f[16][16][16][16][16][6];
    int bkt[6], k, x;

    ll dfs(int c1, int c2, int c3, int c4, int c5, int l)
    {
    if(~f[c1][c2][c3][c4][c5][l]) return f[c1][c2][c3][c4][c5][l];
    ll tot = 0;
    if(!(c1 | c2 | c3 | c4 | c5)) return 1;
    if(c1)
    (tot += (c1 - (l == 2)) * dfs(c1-1,c2,c3,c4,c5,1)) %= mod;
    if(c2)
    (tot += (c2 - (l == 3)) * dfs(c1+1,c2-1,c3,c4,c5,2)) %= mod;
    if(c3)
    (tot += (c3 - (l == 4)) * dfs(c1,c2+1,c3-1,c4,c5,3)) %= mod;
    if(c4)
    (tot += (c4 - (l == 5)) * dfs(c1,c2,c3+1,c4-1,c5,4)) %= mod;
    if(c5)
    (tot += (c5) * dfs(c1,c2,c3,c4+1,c5-1,5)) %= mod;
    return f[c1][c2][c3][c4][c5][l] = tot%mod;
    }

    int main()
    {
    memset(f, -1, sizeof f);
    scanf("%d", &k);
    for(int i = 1; i <= k; ++i)
    scanf("%d", &x), bkt[x]++;
    printf("%lld\n", dfs(bkt[1], bkt[2], bkt[3], bkt[4], bkt[5], 0));
    return 0;
    }

    BZOJ 1511 OKR

    Description

    给定一个串, 求它的每个前缀, 最大周期是多长.

    Solution

    Bouvardia居然手玩出来了.

    观察样例, 再感性理解一下, 其实一直跳nxt到不能再跳为止就是答案.

    但这样只能拿到40分.

    为了加速跳nxt的过程, 我们每次跳完之后就直接修改nxt到最靠前的位置, 类似网络流中当前弧优化的做法.

    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
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>

    using namespace std;

    const int maxn = 1e+6 + 5;

    typedef long long ll;

    int k, nxt[maxn];
    ll len;
    char s[maxn];

    inline void getnxt()
    {
    for(int i = 2; i <= k; ++i)
    {
    int j = nxt[i - 1];
    while(j && s[j + 1] != s[i]) j = nxt[j];
    if(s[j + 1] == s[i]) nxt[i] = j + 1;
    else nxt[i] = 0;
    }
    }

    int main()
    {
    scanf("%d", &k); scanf("%s", s + 1);
    getnxt();
    for(int i = 1; i <= k; ++i)
    {
    int j = i;
    //printf("-%d\n", nxt[i]);
    while(nxt[j]) j = nxt[j];
    if(nxt[i]) nxt[i] = j;
    //printf("%d\n", tmp);
    len += (i - j);
    }
    printf("%lld\n", len);
    return 0;
    }