Processing math: 8%

「Fortuna OJ」May 6 省选 A 组 – 解题报告

A – Cow Gymnasts

规律题。只要能发现合法序列的极差不超过 1 这个性质就可以算了。直接:

ans = 2 – n + \sum_{i = 1}^{n – 1} 2^{\gcd(i, n)}

然后这个显然可以用某些 Polya 题的方式用数论函数进行优化,就没了。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// gymnasts.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1e5 + 200, mod = 1e9 + 7;
typedef long long ll;
ll n, acc, ans;
vector<pair<ll, int>> nset;
void fileIO(string src)
{
freopen((src + ".in").c_str(), "r", stdin);
freopen((src + ".out").c_str(), "w", stdout);
}
ll fpow(ll bas, ll tim)
{
ll ret = 1;
while (tim)
{
if (tim & 1)
ret = 1LL * ret * bas % mod;
bas = 1LL * bas * bas % mod;
tim >>= 1;
}
return ret;
}
void dfs(int pos, ll gcd, ll phiup, ll phidown)
{
if (pos == int(nset.size()))
{
if (gcd == 1)
return;
ans += fpow(2, (n / gcd) % (mod - 1)) * ((1LL * gcd / phidown % mod * phiup % mod) % mod) % mod;
ans %= mod;
return;
}
dfs(pos + 1, gcd, phiup, phidown);
phiup = 1LL * phiup * (nset[pos].first - 1) % mod;
phidown = 1LL * phidown * nset[pos].first;
for (int i = 1; i <= nset[pos].second; i++)
{
gcd *= nset[pos].first;
dfs(pos + 1, gcd, phiup, phidown);
}
}
int main()
{
// fileIO("gymnasts");
scanf("%lld", &n), acc = n;
for (ll i = 2; 1LL * i * i <= acc; i++)
if (acc % i == 0)
{
int cnt = 0;
while (acc % i == 0)
cnt++, acc /= i;
nset.push_back(make_pair(i, cnt));
}
if (acc > 1)
nset.push_back(make_pair(acc, 1));
dfs(0, 1, 1, 1);
printf("%lld\n", (2LL + ans - (n % mod) + mod) % mod);
return 0;
}
// gymnasts.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1e5 + 200, mod = 1e9 + 7; typedef long long ll; ll n, acc, ans; vector<pair<ll, int>> nset; void fileIO(string src) { freopen((src + ".in").c_str(), "r", stdin); freopen((src + ".out").c_str(), "w", stdout); } ll fpow(ll bas, ll tim) { ll ret = 1; while (tim) { if (tim & 1) ret = 1LL * ret * bas % mod; bas = 1LL * bas * bas % mod; tim >>= 1; } return ret; } void dfs(int pos, ll gcd, ll phiup, ll phidown) { if (pos == int(nset.size())) { if (gcd == 1) return; ans += fpow(2, (n / gcd) % (mod - 1)) * ((1LL * gcd / phidown % mod * phiup % mod) % mod) % mod; ans %= mod; return; } dfs(pos + 1, gcd, phiup, phidown); phiup = 1LL * phiup * (nset[pos].first - 1) % mod; phidown = 1LL * phidown * nset[pos].first; for (int i = 1; i <= nset[pos].second; i++) { gcd *= nset[pos].first; dfs(pos + 1, gcd, phiup, phidown); } } int main() { // fileIO("gymnasts"); scanf("%lld", &n), acc = n; for (ll i = 2; 1LL * i * i <= acc; i++) if (acc % i == 0) { int cnt = 0; while (acc % i == 0) cnt++, acc /= i; nset.push_back(make_pair(i, cnt)); } if (acc > 1) nset.push_back(make_pair(acc, 1)); dfs(0, 1, 1, 1); printf("%lld\n", (2LL + ans - (n % mod) + mod) % mod); return 0; }
// gymnasts.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e5 + 200, mod = 1e9 + 7;

typedef long long ll;

ll n, acc, ans;
vector<pair<ll, int>> nset;

void fileIO(string src)
{
    freopen((src + ".in").c_str(), "r", stdin);
    freopen((src + ".out").c_str(), "w", stdout);
}

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

void dfs(int pos, ll gcd, ll phiup, ll phidown)
{
    if (pos == int(nset.size()))
    {
        if (gcd == 1)
            return;
        ans += fpow(2, (n / gcd) % (mod - 1)) * ((1LL * gcd / phidown % mod * phiup % mod) % mod) % mod;
        ans %= mod;
        return;
    }
    dfs(pos + 1, gcd, phiup, phidown);
    phiup = 1LL * phiup * (nset[pos].first - 1) % mod;
    phidown = 1LL * phidown * nset[pos].first;
    for (int i = 1; i <= nset[pos].second; i++)
    {
        gcd *= nset[pos].first;
        dfs(pos + 1, gcd, phiup, phidown);
    }
}

int main()
{
    // fileIO("gymnasts");
    scanf("%lld", &n), acc = n;
    for (ll i = 2; 1LL * i * i <= acc; i++)
        if (acc % i == 0)
        {
            int cnt = 0;
            while (acc % i == 0)
                cnt++, acc /= i;
            nset.push_back(make_pair(i, cnt));
        }
    if (acc > 1)
        nset.push_back(make_pair(acc, 1));
    dfs(0, 1, 1, 1);
    printf("%lld\n", (2LL + ans - (n % mod) + mod) % mod);
    return 0;
}

B – Cow Date

这个题比较有意思。我们考虑化简式子:

f_{l, r} = \prod_{i = l}^r (1 – p_i) \sum_{i = l}^r \frac{p_i}{1 – p_i}

假设是从 [l, r – 1] 处转移而来,那么:

\begin{aligned} f_{l, r} &= (1 – p_r) \prod_{i = l}^{r – 1} (1 – p_i) (\frac{p_r}{1 – p_r} + \sum_{i = l}^{r – 1} \frac{p_i}{1 – p_i}) \\ &= (1 – p_r) \prod_{i = l}^{r – 1} (1 – p_i) \sum_{i = l}^{r – 1} \frac{p_i}{1 – p_i} + p_r \prod_{i = l}^{r – 1} (1 – p_i) \\ &= (1 – p_r) f_{l, r – 1} + p_r \prod_{i = l}^{r – 1} (1 – p_i) \end{aligned}

显然可以考虑 Two-Pointers,然后考虑多出来的那一坨是否会使得答案更优即可。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// cowdate.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1000000 + 200;
typedef long double ld;
int n, pi[MAX_N], ans;
void fileIO(string src)
{
freopen((src + ".in").c_str(), "r", stdin);
freopen((src + ".out").c_str(), "w", stdout);
}
int main()
{
// fileIO("cowdate");
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &pi[i]), ans = max(ans, pi[i]);
if (ans >= 5e5)
printf("%d\n", ans), exit(0);
int rptr = 1;
ld acc = pi[1], prod = 1000000 - pi[1];
while (rptr < n && prod > acc)
{
rptr++, acc = acc * (1000000 - pi[rptr]) / 1000000 + pi[rptr] * prod / 1000000;
prod = prod * (1000000 - pi[rptr]) / 1000000;
}
ans = max(ans, int(acc));
for (int lptr = 2; lptr <= n; lptr++)
{
prod = prod * 1000000 / (1000000 - pi[lptr - 1]);
acc = (acc * 1000000 - pi[lptr - 1] * prod) / (1000000 - pi[lptr - 1]);
while (rptr < n && prod > acc)
{
rptr++, acc = acc * (1000000 - pi[rptr]) / 1000000 + pi[rptr] * prod / 1000000;
prod = prod * (1000000 - pi[rptr]) / 1000000;
}
ans = max(ans, int(acc));
}
printf("%d\n", ans);
return 0;
}
// cowdate.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1000000 + 200; typedef long double ld; int n, pi[MAX_N], ans; void fileIO(string src) { freopen((src + ".in").c_str(), "r", stdin); freopen((src + ".out").c_str(), "w", stdout); } int main() { // fileIO("cowdate"); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &pi[i]), ans = max(ans, pi[i]); if (ans >= 5e5) printf("%d\n", ans), exit(0); int rptr = 1; ld acc = pi[1], prod = 1000000 - pi[1]; while (rptr < n && prod > acc) { rptr++, acc = acc * (1000000 - pi[rptr]) / 1000000 + pi[rptr] * prod / 1000000; prod = prod * (1000000 - pi[rptr]) / 1000000; } ans = max(ans, int(acc)); for (int lptr = 2; lptr <= n; lptr++) { prod = prod * 1000000 / (1000000 - pi[lptr - 1]); acc = (acc * 1000000 - pi[lptr - 1] * prod) / (1000000 - pi[lptr - 1]); while (rptr < n && prod > acc) { rptr++, acc = acc * (1000000 - pi[rptr]) / 1000000 + pi[rptr] * prod / 1000000; prod = prod * (1000000 - pi[rptr]) / 1000000; } ans = max(ans, int(acc)); } printf("%d\n", ans); return 0; }
// cowdate.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1000000 + 200;

typedef long double ld;

int n, pi[MAX_N], ans;

void fileIO(string src)
{
    freopen((src + ".in").c_str(), "r", stdin);
    freopen((src + ".out").c_str(), "w", stdout);
}

int main()
{
    // fileIO("cowdate");
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &pi[i]), ans = max(ans, pi[i]);
    if (ans >= 5e5)
        printf("%d\n", ans), exit(0);
    int rptr = 1;
    ld acc = pi[1], prod = 1000000 - pi[1];
    while (rptr < n && prod > acc)
    {
        rptr++, acc = acc * (1000000 - pi[rptr]) / 1000000 + pi[rptr] * prod / 1000000;
        prod = prod * (1000000 - pi[rptr]) / 1000000;
    }
    ans = max(ans, int(acc));
    for (int lptr = 2; lptr <= n; lptr++)
    {
        prod = prod * 1000000 / (1000000 - pi[lptr - 1]);
        acc = (acc * 1000000 - pi[lptr - 1] * prod) / (1000000 - pi[lptr - 1]);
        while (rptr < n && prod > acc)
        {
            rptr++, acc = acc * (1000000 - pi[rptr]) / 1000000 + pi[rptr] * prod / 1000000;
            prod = prod * (1000000 - pi[rptr]) / 1000000;
        }
        ans = max(ans, int(acc));
    }
    printf("%d\n", ans);
    return 0;
}

C – Moorio Kart

妈的,这题 \Theta(n^3) 在考场上送了(写得太复杂了我真的是sb)。

首先,众所周知,这道题需要你把一堆树串起来,然后计算所有的、大于 y 的环长。正常来讲这个 \Theta(n^3) 的背包可以做,然后搞一个圆排列数即可。

复杂度还是太大了。然而,如果你做背包的时候做一个小剪枝,那么复杂度就会降低到 \Theta(n^2 \sqrt n) 的级别。考虑合并,每个块内的路径个数为 \Theta(k^2)、背包大小为 Y – Xk。这两个东西乘起来最糟糕的情况就是 \Theta(n^2 \sqrt n) 的级别。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// mooriokart.cpp
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 1.5e3 + 200, mod = 1e9 + 7;
typedef long long ll;
int n, m, X, Y, head[MAX_N], current, dist[MAX_N][MAX_N], idx[MAX_N], mem[MAX_N];
int coltot, ans, prod[MAX_N][2600], tot[MAX_N][2600], F[2600][2], tmp[2600][2];
void fileIO(string src)
{
freopen((src + ".in").c_str(), "r", stdin);
freopen((src + ".out").c_str(), "w", stdout);
}
struct edge
{
int to, nxt, weight;
} edges[MAX_N << 1];
void addpath(int src, int dst, int weight)
{
edges[current].to = dst, edges[current].nxt = head[src];
edges[current].weight = weight, head[src] = current++;
}
int find(int x) { return x == mem[x] ? x : mem[x] = find(mem[x]); }
void dfs(int u, int fa, int org, int dep)
{
if (fa)
{
prod[idx[u]][min(dep, Y)] = (0LL + prod[idx[u]][min(dep, Y)] + dep) % mod;
tot[idx[u]][min(dep, Y)]++;
}
dist[org][u] = dep;
for (int i = head[u]; i != -1; i = edges[i].nxt)
if (edges[i].to != fa)
dfs(edges[i].to, u, org, dep + edges[i].weight);
}
int main()
{
// fileIO("mooriokart");
memset(head, -1, sizeof(head)), memset(dist, -1, sizeof(dist));
scanf("%d%d%d%d", &n, &m, &X, &Y);
for (int i = 1; i <= n; i++)
mem[i] = i;
for (int i = 1, u, v, w; i <= m; i++)
{
scanf("%d%d%d", &u, &v, &w), addpath(u, v, w), addpath(v, u, w);
if (find(u) != find(v))
mem[find(u)] = find(v);
}
for (int i = 1; i <= n; i++)
{
if (idx[find(i)] == 0)
idx[find(i)] = ++coltot;
idx[i] = idx[find(i)];
}
for (int i = 1; i <= n; i++)
dfs(i, 0, i, 0);
int base = min(Y, coltot * X);
F[base][0] = 1, F[base][1] = coltot * X;
for (int i = 1; i <= coltot; i++)
{
for (int j = base; j <= Y; j++)
tmp[j][0] = tmp[j][1] = 0;
for (int j = 0; j <= Y; j++)
if (tot[i][j])
for (int k = base; k <= Y; k++)
if (F[k][0])
{
tmp[min(j + k, Y)][0] = (0LL + tmp[min(j + k, Y)][0] + 1LL * tot[i][j] * F[k][0] % mod) % mod;
tmp[min(j + k, Y)][1] = (0LL + tmp[min(j + k, Y)][1] + 1LL * prod[i][j] * F[k][0] % mod + 1LL * F[k][1] * tot[i][j] % mod) % mod;
}
memcpy(F, tmp, sizeof(tmp));
}
ans = F[Y][1];
for (int i = 1; i < coltot; i++)
ans = 1LL * ans * i % mod;
printf("%lld\n", 1LL * ans * ((mod + 1) >> 1) % mod);
return 0;
}
// mooriokart.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 1.5e3 + 200, mod = 1e9 + 7; typedef long long ll; int n, m, X, Y, head[MAX_N], current, dist[MAX_N][MAX_N], idx[MAX_N], mem[MAX_N]; int coltot, ans, prod[MAX_N][2600], tot[MAX_N][2600], F[2600][2], tmp[2600][2]; void fileIO(string src) { freopen((src + ".in").c_str(), "r", stdin); freopen((src + ".out").c_str(), "w", stdout); } struct edge { int to, nxt, weight; } edges[MAX_N << 1]; void addpath(int src, int dst, int weight) { edges[current].to = dst, edges[current].nxt = head[src]; edges[current].weight = weight, head[src] = current++; } int find(int x) { return x == mem[x] ? x : mem[x] = find(mem[x]); } void dfs(int u, int fa, int org, int dep) { if (fa) { prod[idx[u]][min(dep, Y)] = (0LL + prod[idx[u]][min(dep, Y)] + dep) % mod; tot[idx[u]][min(dep, Y)]++; } dist[org][u] = dep; for (int i = head[u]; i != -1; i = edges[i].nxt) if (edges[i].to != fa) dfs(edges[i].to, u, org, dep + edges[i].weight); } int main() { // fileIO("mooriokart"); memset(head, -1, sizeof(head)), memset(dist, -1, sizeof(dist)); scanf("%d%d%d%d", &n, &m, &X, &Y); for (int i = 1; i <= n; i++) mem[i] = i; for (int i = 1, u, v, w; i <= m; i++) { scanf("%d%d%d", &u, &v, &w), addpath(u, v, w), addpath(v, u, w); if (find(u) != find(v)) mem[find(u)] = find(v); } for (int i = 1; i <= n; i++) { if (idx[find(i)] == 0) idx[find(i)] = ++coltot; idx[i] = idx[find(i)]; } for (int i = 1; i <= n; i++) dfs(i, 0, i, 0); int base = min(Y, coltot * X); F[base][0] = 1, F[base][1] = coltot * X; for (int i = 1; i <= coltot; i++) { for (int j = base; j <= Y; j++) tmp[j][0] = tmp[j][1] = 0; for (int j = 0; j <= Y; j++) if (tot[i][j]) for (int k = base; k <= Y; k++) if (F[k][0]) { tmp[min(j + k, Y)][0] = (0LL + tmp[min(j + k, Y)][0] + 1LL * tot[i][j] * F[k][0] % mod) % mod; tmp[min(j + k, Y)][1] = (0LL + tmp[min(j + k, Y)][1] + 1LL * prod[i][j] * F[k][0] % mod + 1LL * F[k][1] * tot[i][j] % mod) % mod; } memcpy(F, tmp, sizeof(tmp)); } ans = F[Y][1]; for (int i = 1; i < coltot; i++) ans = 1LL * ans * i % mod; printf("%lld\n", 1LL * ans * ((mod + 1) >> 1) % mod); return 0; }
// mooriokart.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1.5e3 + 200, mod = 1e9 + 7;

typedef long long ll;

int n, m, X, Y, head[MAX_N], current, dist[MAX_N][MAX_N], idx[MAX_N], mem[MAX_N];
int coltot, ans, prod[MAX_N][2600], tot[MAX_N][2600], F[2600][2], tmp[2600][2];

void fileIO(string src)
{
    freopen((src + ".in").c_str(), "r", stdin);
    freopen((src + ".out").c_str(), "w", stdout);
}

struct edge
{
    int to, nxt, weight;
} edges[MAX_N << 1];

void addpath(int src, int dst, int weight)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    edges[current].weight = weight, head[src] = current++;
}

int find(int x) { return x == mem[x] ? x : mem[x] = find(mem[x]); }

void dfs(int u, int fa, int org, int dep)
{
    if (fa)
    {
        prod[idx[u]][min(dep, Y)] = (0LL + prod[idx[u]][min(dep, Y)] + dep) % mod;
        tot[idx[u]][min(dep, Y)]++;
    }
    dist[org][u] = dep;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa)
            dfs(edges[i].to, u, org, dep + edges[i].weight);
}

int main()
{
    // fileIO("mooriokart");
    memset(head, -1, sizeof(head)), memset(dist, -1, sizeof(dist));
    scanf("%d%d%d%d", &n, &m, &X, &Y);
    for (int i = 1; i <= n; i++)
        mem[i] = i;
    for (int i = 1, u, v, w; i <= m; i++)
    {
        scanf("%d%d%d", &u, &v, &w), addpath(u, v, w), addpath(v, u, w);
        if (find(u) != find(v))
            mem[find(u)] = find(v);
    }
    for (int i = 1; i <= n; i++)
    {
        if (idx[find(i)] == 0)
            idx[find(i)] = ++coltot;
        idx[i] = idx[find(i)];
    }
    for (int i = 1; i <= n; i++)
        dfs(i, 0, i, 0);
    int base = min(Y, coltot * X);
    F[base][0] = 1, F[base][1] = coltot * X;
    for (int i = 1; i <= coltot; i++)
    {
        for (int j = base; j <= Y; j++)
            tmp[j][0] = tmp[j][1] = 0;
        for (int j = 0; j <= Y; j++)
            if (tot[i][j])
                for (int k = base; k <= Y; k++)
                    if (F[k][0])
                    {
                        tmp[min(j + k, Y)][0] = (0LL + tmp[min(j + k, Y)][0] + 1LL * tot[i][j] * F[k][0] % mod) % mod;
                        tmp[min(j + k, Y)][1] = (0LL + tmp[min(j + k, Y)][1] + 1LL * prod[i][j] * F[k][0] % mod + 1LL * F[k][1] * tot[i][j] % mod) % mod;
                    }
        memcpy(F, tmp, sizeof(tmp));
    }
    ans = F[Y][1];
    for (int i = 1; i < coltot; i++)
        ans = 1LL * ans * i % mod;
    printf("%lld\n", 1LL * ans * ((mod + 1) >> 1) % mod);
    return 0;
}

Leave a Reply

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