A – Cow Gymnasts
规律题。只要能发现合法序列的极差不超过 1 这个性质就可以算了。直接:
ans = 2 – n + \sum_{i = 1}^{n – 1} 2^{\gcd(i, n)}
然后这个显然可以用某些 Polya 题的方式用数论函数进行优化,就没了。
const int MAX_N = 1e5 + 200, mod = 1e9 + 7;
vector<pair<ll, int>> nset;
freopen((src + ".in").c_str(), "r", stdin);
freopen((src + ".out").c_str(), "w", stdout);
ret = 1LL * ret * bas % mod;
bas = 1LL * bas * bas % mod;
void dfs(int pos, ll gcd, ll phiup, ll phidown)
if (pos == int(nset.size()))
ans += fpow(2, (n / gcd) % (mod - 1)) * ((1LL * gcd / phidown % mod * phiup % mod) % mod) % mod;
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++)
dfs(pos + 1, gcd, phiup, phidown);
scanf("%lld", &n), acc = n;
for (ll i = 2; 1LL * i * i <= acc; i++)
nset.push_back(make_pair(i, cnt));
nset.push_back(make_pair(acc, 1));
printf("%lld\n", (2LL + ans - (n % mod) + mod) % mod);
// 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,然后考虑多出来的那一坨是否会使得答案更优即可。
const int MAX_N = 1000000 + 200;
freopen((src + ".in").c_str(), "r", stdin);
freopen((src + ".out").c_str(), "w", stdout);
for (int i = 1; i <= n; i++)
scanf("%d", &pi[i]), ans = max(ans, pi[i]);
printf("%d\n", ans), exit(0);
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));
// 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) 的级别。
const int MAX_N = 1.5e3 + 200, mod = 1e9 + 7;
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];
freopen((src + ".in").c_str(), "r", stdin);
freopen((src + ".out").c_str(), "w", stdout);
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)
prod[idx[u]][min(dep, Y)] = (0LL + prod[idx[u]][min(dep, Y)] + dep) % mod;
tot[idx[u]][min(dep, Y)]++;
for (int i = head[u]; i != -1; i = edges[i].nxt)
dfs(edges[i].to, u, org, dep + edges[i].weight);
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++)
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);
for (int i = 1; i <= n; i++)
for (int i = 1; i <= n; i++)
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++)
for (int k = base; k <= Y; k++)
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));
for (int i = 1; i < coltot; i++)
ans = 1LL * ans * i % mod;
printf("%lld\n", 1LL * ans * ((mod + 1) >> 1) % mod);
// 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;
}