Loading [MathJax]/extensions/tex2jax.js

「Fortuna OJ」Apr 15 省选组 – 解题报告

A – 楼房搭建

首先这题的 \(\Theta(n^3)\) 是很好写的,如果要写到 \(\Theta(n^2)\) 的分,我们可以观察到 DP 的过程其实是一个做后缀 min 的过程,所以优化完毕(当然也可以线性规划,但是很难写)。

正解比较神仙。考虑做到第 \(i\) 个时,考虑 \((2, 1)\) 的操作做满。显然这是局部最优解而不是全局最优解,然而我们考虑去反悔:考虑把 \((2, 1)\) 换成 \((1, 2)\),在用额外的一个时间去做一个 \((1, 2)\),那么我们可以在 \(i – 1\) 不动的情况下让 \(i\) 加上三个单位。其他的反悔方式类似。

大概就是这样的思路,我们可以记录上一次的次数然后贪心的换即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// building.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e6 + 200;
int n, hi[MAX_N], ai[MAX_N];
inline char nc()
{
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}
int read()
{
int x = 0, f = 1;
char ch = nc();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = nc();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = nc();
return x * f;
}
void fileIO()
{
freopen("building.in", "r", stdin);
freopen("building.out", "w", stdout);
}
int main()
{
// fileIO();
n = read();
for (int i = 1; i <= n; i++)
hi[i] = read();
long long ans = 0;
for (int i = 1; i <= n; i++)
{
hi[i] = max(hi[i], 0);
int regret3 = min(hi[i] / 3, ai[i]);
hi[i] -= regret3 * 3;
int regret2 = hi[i] >> 1;
hi[i] -= regret2 * 2;
int regret1 = hi[i];
hi[i] = 0, ans += regret1 + regret2 + regret3;
ai[i + 1] += regret3 * 2 + regret2;
hi[i + 1] -= regret2 + regret1 * 2;
}
printf("%lld\n", ans);
return 0;
}
// building.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e6 + 200; int n, hi[MAX_N], ai[MAX_N]; inline char nc() { static char buf[100000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++; } int read() { int x = 0, f = 1; char ch = nc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = nc(); } while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = nc(); return x * f; } void fileIO() { freopen("building.in", "r", stdin); freopen("building.out", "w", stdout); } int main() { // fileIO(); n = read(); for (int i = 1; i <= n; i++) hi[i] = read(); long long ans = 0; for (int i = 1; i <= n; i++) { hi[i] = max(hi[i], 0); int regret3 = min(hi[i] / 3, ai[i]); hi[i] -= regret3 * 3; int regret2 = hi[i] >> 1; hi[i] -= regret2 * 2; int regret1 = hi[i]; hi[i] = 0, ans += regret1 + regret2 + regret3; ai[i + 1] += regret3 * 2 + regret2; hi[i + 1] -= regret2 + regret1 * 2; } printf("%lld\n", ans); return 0; }
// building.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e6 + 200;

int n, hi[MAX_N], ai[MAX_N];

inline char nc()
{
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

int read()
{
    int x = 0, f = 1;
    char ch = nc();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = nc();
    }
    while (isdigit(ch))
        x = (x << 3) + (x << 1) + ch - '0', ch = nc();
    return x * f;
}

void fileIO()
{
    freopen("building.in", "r", stdin);
    freopen("building.out", "w", stdout);
}

int main()
{
    // fileIO();
    n = read();
    for (int i = 1; i <= n; i++)
        hi[i] = read();
    long long ans = 0;
    for (int i = 1; i <= n; i++)
    {
        hi[i] = max(hi[i], 0);
        int regret3 = min(hi[i] / 3, ai[i]);
        hi[i] -= regret3 * 3;
        int regret2 = hi[i] >> 1;
        hi[i] -= regret2 * 2;
        int regret1 = hi[i];
        hi[i] = 0, ans += regret1 + regret2 + regret3;
        ai[i + 1] += regret3 * 2 + regret2;
        hi[i + 1] -= regret2 + regret1 * 2;
    }
    printf("%lld\n", ans);
    return 0;
}

B – 手链强化

终于碰到了 Burnside 和 Polya 的题了。

首先这里的置换操作只有一个旋转,所以循环节是 \(\gcd\),所以我们只需要来算 \(\gcd\) 个等价类时的方案数。

其实等价类方案数也很好求,设 \(f[i][0], f[i][1]\) 为考虑过 \(i\) 个等价类、位置上有没有染色的方案数,直接 \(f[i][0] = f[i – 1][1] + f[i – 1][0], f[i][1] = k f[i – 1][0]\),并且发现这个矩阵快速幂也很好做。

还要注意一点,就是开头的等价类和末尾的等价类不能相邻,所以分类考虑出方案数为 \(f[t – 1][0] + f[t – 1][1] + f[t – 2][0]\)。

最后答案就是:

\[ \frac{1}{n} \sum_{d|n} (f[d – 1][0] + f[d – 1][1] + f[d – 2][0]) \varphi(\frac{n}{d}) \]

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@@ -0,0 +1,103 @@
// bracelet.cpp
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int n, kind, dp[1010][2][2];
void fileIO()
{
freopen("bracelet.in", "r", stdin);
freopen("bracelet.out", "w", stdout);
}
struct matrix
{
int mat[2][2];
int *operator[](const int &idx) { return mat[idx]; }
void clear() { memset(mat, 0, sizeof(mat)); }
matrix operator*(const matrix &rhs)
{
matrix ret;
ret.clear();
for (int i = 0; i < 2; i++)
for (int k = 0; k < 2; k++)
if (mat[i][k])
for (int j = 0; j < 2; j++)
if (rhs.mat[k][j])
ret[i][j] = (0LL + ret[i][j] + 1LL * mat[i][k] * rhs.mat[k][j] % mod) % mod;
return ret;
}
matrix operator^(const int &idx)
{
int tim = idx;
matrix ret, bas = *this;
ret.clear(), ret[0][0] = ret[1][1] = 1;
while (tim)
{
if (tim & 1)
ret = ret * bas;
bas = bas * bas;
tim >>= 1;
}
return ret;
}
} trans, init;
matrix getFn(int x) { return init * (trans ^ x); }
int phi(int x)
{
int ret = x;
for (int i = 2; 1LL * i * i <= x; i++)
if (x % i == 0)
{
ret -= ret / i;
while (x % i == 0)
x /= i;
}
if (x != 1)
ret -= ret / x;
return ret % mod;
}
int fpow(int bas, int tim)
{
int ret = 1;
while (tim)
{
if (tim & 1)
ret = 1LL * ret * bas % mod;
bas = 1LL * bas * bas % mod;
tim >>= 1;
}
return ret;
}
const int inv2 = fpow(2, mod - 2);
int solve(int x)
{
if (x == 1)
return phi(n / x);
matrix c = getFn(x - 2);
int pans = 1LL * kind * c[0][0] % mod;
c = c * trans;
pans = (0LL + pans + c[0][0] + c[0][1]) % mod;
pans = 1LL * pans * phi(n / x) % mod;
return pans;
}
int main()
{
// fileIO();
scanf("%d%d", &n, &kind), init[0][0] = 1, trans[0][0] = trans[1][0] = 1, trans[0][1] = kind;
int ans = 0;
for (int i = 1; 1LL * i * i <= n; i++)
if (n % i == 0)
ans = (0LL + ans + solve(i) + ((1LL * i * i == n) ? 0 : solve(n / i))) % mod;
printf("%lld\n", 1LL * ans * fpow(n, mod - 2) % mod);
return 0;
}
@@ -0,0 +1,103 @@ // bracelet.cpp #include <bits/stdc++.h> using namespace std; const int mod = 1e9 + 7; int n, kind, dp[1010][2][2]; void fileIO() { freopen("bracelet.in", "r", stdin); freopen("bracelet.out", "w", stdout); } struct matrix { int mat[2][2]; int *operator[](const int &idx) { return mat[idx]; } void clear() { memset(mat, 0, sizeof(mat)); } matrix operator*(const matrix &rhs) { matrix ret; ret.clear(); for (int i = 0; i < 2; i++) for (int k = 0; k < 2; k++) if (mat[i][k]) for (int j = 0; j < 2; j++) if (rhs.mat[k][j]) ret[i][j] = (0LL + ret[i][j] + 1LL * mat[i][k] * rhs.mat[k][j] % mod) % mod; return ret; } matrix operator^(const int &idx) { int tim = idx; matrix ret, bas = *this; ret.clear(), ret[0][0] = ret[1][1] = 1; while (tim) { if (tim & 1) ret = ret * bas; bas = bas * bas; tim >>= 1; } return ret; } } trans, init; matrix getFn(int x) { return init * (trans ^ x); } int phi(int x) { int ret = x; for (int i = 2; 1LL * i * i <= x; i++) if (x % i == 0) { ret -= ret / i; while (x % i == 0) x /= i; } if (x != 1) ret -= ret / x; return ret % mod; } int fpow(int bas, int tim) { int ret = 1; while (tim) { if (tim & 1) ret = 1LL * ret * bas % mod; bas = 1LL * bas * bas % mod; tim >>= 1; } return ret; } const int inv2 = fpow(2, mod - 2); int solve(int x) { if (x == 1) return phi(n / x); matrix c = getFn(x - 2); int pans = 1LL * kind * c[0][0] % mod; c = c * trans; pans = (0LL + pans + c[0][0] + c[0][1]) % mod; pans = 1LL * pans * phi(n / x) % mod; return pans; } int main() { // fileIO(); scanf("%d%d", &n, &kind), init[0][0] = 1, trans[0][0] = trans[1][0] = 1, trans[0][1] = kind; int ans = 0; for (int i = 1; 1LL * i * i <= n; i++) if (n % i == 0) ans = (0LL + ans + solve(i) + ((1LL * i * i == n) ? 0 : solve(n / i))) % mod; printf("%lld\n", 1LL * ans * fpow(n, mod - 2) % mod); return 0; }
@@ -0,0 +1,103 @@
// bracelet.cpp
#include <bits/stdc++.h>

using namespace std;

const int mod = 1e9 + 7;

int n, kind, dp[1010][2][2];

void fileIO()
{
    freopen("bracelet.in", "r", stdin);
    freopen("bracelet.out", "w", stdout);
}

struct matrix
{
    int mat[2][2];
    int *operator[](const int &idx) { return mat[idx]; }
    void clear() { memset(mat, 0, sizeof(mat)); }
    matrix operator*(const matrix &rhs)
    {
        matrix ret;
        ret.clear();
        for (int i = 0; i < 2; i++)
            for (int k = 0; k < 2; k++)
                if (mat[i][k])
                    for (int j = 0; j < 2; j++)
                        if (rhs.mat[k][j])
                            ret[i][j] = (0LL + ret[i][j] + 1LL * mat[i][k] * rhs.mat[k][j] % mod) % mod;
        return ret;
    }
    matrix operator^(const int &idx)
    {
        int tim = idx;
        matrix ret, bas = *this;
        ret.clear(), ret[0][0] = ret[1][1] = 1;
        while (tim)
        {
            if (tim & 1)
                ret = ret * bas;
            bas = bas * bas;
            tim >>= 1;
        }
        return ret;
    }
} trans, init;

matrix getFn(int x) { return init * (trans ^ x); }

int phi(int x)
{
    int ret = x;
    for (int i = 2; 1LL * i * i <= x; i++)
        if (x % i == 0)
        {
            ret -= ret / i;
            while (x % i == 0)
                x /= i;
        }
    if (x != 1)
        ret -= ret / x;
    return ret % mod;
}

int fpow(int bas, int tim)
{
    int ret = 1;
    while (tim)
    {
        if (tim & 1)
            ret = 1LL * ret * bas % mod;
        bas = 1LL * bas * bas % mod;
        tim >>= 1;
    }
    return ret;
}

const int inv2 = fpow(2, mod - 2);

int solve(int x)
{
    if (x == 1)
        return phi(n / x);
    matrix c = getFn(x - 2);
    int pans = 1LL * kind * c[0][0] % mod;
    c = c * trans;
    pans = (0LL + pans + c[0][0] + c[0][1]) % mod;
    pans = 1LL * pans * phi(n / x) % mod;
    return pans;
}

int main()
{
    // fileIO();
    scanf("%d%d", &n, &kind), init[0][0] = 1, trans[0][0] = trans[1][0] = 1, trans[0][1] = kind;
    int ans = 0;
    for (int i = 1; 1LL * i * i <= n; i++)
        if (n % i == 0)
            ans = (0LL + ans + solve(i) + ((1LL * i * i == n) ? 0 : solve(n / i))) % mod;
    printf("%lld\n", 1LL * ans * fpow(n, mod - 2) % mod);
    return 0;
}

C – 数字收藏

不难,发现贡献是莫比乌斯函数乘上已有倍数个数,所以写完了。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@@ -0,0 +1,104 @@
// number.cpp
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 200;
typedef long long ll;
int n, d, mu[MAX_N], primes[MAX_N], tot, psiz[MAX_N];
bool vis[MAX_N];
multiset<int> pos;
ll ans;
void fileIO()
{
freopen("number.in", "r", stdin);
freopen("number.out", "w", stdout);
}
inline char nc()
{
static char buf[1000000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
}
int read()
{
int x = 0, f = 1;
char ch = nc();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = nc();
}
while (isdigit(ch))
x = (x << 3) + (x << 1) + ch - '0', ch = nc();
return x * f;
}
int main()
{
// fileIO();
n = read(), d = read();
mu[1] = 1;
for (int i = 2; i < MAX_N; i++)
{
if (!vis[i])
primes[++tot] = i, mu[i] = -1;
for (int j = 1; j <= tot && 1LL * i * primes[j] < MAX_N; j++)
{
vis[i * primes[j]] = true, mu[i * primes[j]] = -mu[i];
if (i % primes[j] == 0)
{
mu[i * primes[j]] = 0;
break;
}
}
}
// printf("%.6lf\n", 1.0 * clock() / CLOCKS_PER_SEC);
// answer the queries;
while (n--)
{
int opt = read(), x = read();
if (x % d != 0)
{
printf("%lld\n", ans);
continue;
}
x /= d;
if (opt == 1)
{
pos.insert(x);
for (int i = 1; 1LL * i * i <= x; i++)
if (x % i == 0)
{
ans += mu[i] * psiz[i], psiz[i]++;
if (x / i != i)
ans += mu[x / i] * psiz[x / i], psiz[x / i]++;
}
}
else
{
if (pos.find(x) == pos.end())
{
printf("%lld\n", ans);
continue;
}
pos.erase(pos.find(x));
for (int i = 1; 1LL * i * i <= x; i++)
if (x % i == 0)
{
psiz[i]--, ans -= mu[i] * psiz[i];
if (x / i != i)
psiz[x / i]--, ans -= mu[x / i] * psiz[x / i];
}
}
printf("%lld\n", ans);
}
// printf("%.6lf\n", 1.0 * clock() / CLOCKS_PER_SEC);
return 0;
}
@@ -0,0 +1,104 @@ // number.cpp #pragma GCC optimize(2) #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e5 + 200; typedef long long ll; int n, d, mu[MAX_N], primes[MAX_N], tot, psiz[MAX_N]; bool vis[MAX_N]; multiset<int> pos; ll ans; void fileIO() { freopen("number.in", "r", stdin); freopen("number.out", "w", stdout); } inline char nc() { static char buf[1000000], *p1 = buf, *p2 = buf; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++; } int read() { int x = 0, f = 1; char ch = nc(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = nc(); } while (isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = nc(); return x * f; } int main() { // fileIO(); n = read(), d = read(); mu[1] = 1; for (int i = 2; i < MAX_N; i++) { if (!vis[i]) primes[++tot] = i, mu[i] = -1; for (int j = 1; j <= tot && 1LL * i * primes[j] < MAX_N; j++) { vis[i * primes[j]] = true, mu[i * primes[j]] = -mu[i]; if (i % primes[j] == 0) { mu[i * primes[j]] = 0; break; } } } // printf("%.6lf\n", 1.0 * clock() / CLOCKS_PER_SEC); // answer the queries; while (n--) { int opt = read(), x = read(); if (x % d != 0) { printf("%lld\n", ans); continue; } x /= d; if (opt == 1) { pos.insert(x); for (int i = 1; 1LL * i * i <= x; i++) if (x % i == 0) { ans += mu[i] * psiz[i], psiz[i]++; if (x / i != i) ans += mu[x / i] * psiz[x / i], psiz[x / i]++; } } else { if (pos.find(x) == pos.end()) { printf("%lld\n", ans); continue; } pos.erase(pos.find(x)); for (int i = 1; 1LL * i * i <= x; i++) if (x % i == 0) { psiz[i]--, ans -= mu[i] * psiz[i]; if (x / i != i) psiz[x / i]--, ans -= mu[x / i] * psiz[x / i]; } } printf("%lld\n", ans); } // printf("%.6lf\n", 1.0 * clock() / CLOCKS_PER_SEC); return 0; }
@@ -0,0 +1,104 @@
// number.cpp
#pragma GCC optimize(2)
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e5 + 200;

typedef long long ll;

int n, d, mu[MAX_N], primes[MAX_N], tot, psiz[MAX_N];
bool vis[MAX_N];
multiset<int> pos;
ll ans;

void fileIO()
{
    freopen("number.in", "r", stdin);
    freopen("number.out", "w", stdout);
}

inline char nc()
{
    static char buf[1000000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
}

int read()
{
    int x = 0, f = 1;
    char ch = nc();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = nc();
    }
    while (isdigit(ch))
        x = (x << 3) + (x << 1) + ch - '0', ch = nc();
    return x * f;
}

int main()
{
    // fileIO();
    n = read(), d = read();
    mu[1] = 1;
    for (int i = 2; i < MAX_N; i++)
    {
        if (!vis[i])
            primes[++tot] = i, mu[i] = -1;
        for (int j = 1; j <= tot && 1LL * i * primes[j] < MAX_N; j++)
        {
            vis[i * primes[j]] = true, mu[i * primes[j]] = -mu[i];
            if (i % primes[j] == 0)
            {
                mu[i * primes[j]] = 0;
                break;
            }
        }
    }
    // printf("%.6lf\n", 1.0 * clock() / CLOCKS_PER_SEC);
    // answer the queries;
    while (n--)
    {
        int opt = read(), x = read();
        if (x % d != 0)
        {
            printf("%lld\n", ans);
            continue;
        }
        x /= d;
        if (opt == 1)
        {
            pos.insert(x);
            for (int i = 1; 1LL * i * i <= x; i++)
                if (x % i == 0)
                {
                    ans += mu[i] * psiz[i], psiz[i]++;
                    if (x / i != i)
                        ans += mu[x / i] * psiz[x / i], psiz[x / i]++;
                }
        }
        else
        {
            if (pos.find(x) == pos.end())
            {
                printf("%lld\n", ans);
                continue;
            }
            pos.erase(pos.find(x));
            for (int i = 1; 1LL * i * i <= x; i++)
                if (x % i == 0)
                {
                    psiz[i]--, ans -= mu[i] * psiz[i];
                    if (x / i != i)
                        psiz[x / i]--, ans -= mu[x / i] * psiz[x / i];
                }
        }
        printf("%lld\n", ans);
    }
    // printf("%.6lf\n", 1.0 * clock() / CLOCKS_PER_SEC);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *