Exotic … Ancient City
注意到值域很小,所以可以对每种 w w w 数只考虑 ≤ w \le w ≤w 的边的连通块个数。维护一个大小为 2 n 2n 2n 的并查集,表示第 i i i 列和第 i + 1 i+1 i+1 列的连通性。考虑右移一列并查集的变化,如果在之前的并查集合并了集合 u + n , v + n u+n, v+n u+n,v+n ,那么在新的并查集上 u u u 和 v v v 会变得连通。如果它们已经连通了,这条边就没用了;否则它们可能新增连通性,对后面有影响,以此类推即可。
#include <bits/stdc++.h>
using namespace std;
const int MAX = 30;
class dsu {
public:
vector<int> p;
int n;
dsu(int n): n(n) {
p.resize(n);
for (int i = 0; i < n; ++i) {
p[i] = i;
}
}
inline int find(int x) {
while (x != p[x]) {
x = p[x] = p[p[x]];
}
return x;
}
inline bool unite(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return false;
} else {
p[x] = y;
return true;
}
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, q;
cin >> n >> m >> q;
vector<vector<pair<int, int>>> edges(MAX);
while (q--) {
int from, to, cost;
cin >> from >> to >> cost;
--from;
--to;
for (int i = cost; i < MAX; ++i) {
edges[i].emplace_back(from, to);
}
}
vector<long long> ans(m);
for (int w = 0; w < MAX; ++w) {
dsu p(n * 2);
vector<long long> tag(m);
vector<pair<int, int>> useful;
for (auto e : edges[w]) {
int x = p.find(e.first), y = p.find(e.second + n);
if (x > y) {
swap(x, y);
}
if (p.unite(x, y)) {
++tag[0];
if (x >= n && y >= n) {
useful.emplace_back(x - n, y - n);
}
}
}
for (int i = 1; i < m && !useful.empty(); ++i) {
vector<pair<int, int>> new_useful;
for (auto e : useful) {
int x = p.find(e.first), y = p.find(e.second);
if (x > y) {
swap(x, y);
}
if (p.unite(x, y)) {
if (x >= n && y >= n) {
new_useful.emplace_back(x - n, y - n);
}
} else {
--tag[i];
}
}
swap(useful, new_useful);
}
for (int i = 1; i < m; ++i) {
tag[i] += tag[i - 1];
}
for (int i = 1; i < m; ++i) {
tag[i] += tag[i - 1];
}
for (int i = 0; i < m; ++i) {
ans[i] += (long long) n * (i + 2) - 1 - tag[i];
}
}
for (int i = 0; i < m; ++i) {
cout << ans[i] << "\n";
}
return 0;
}
Mysterious … Host
考虑一个排列,用尽量少的段去覆盖它,分两种情况讨论:
- 段数 ≥ 3 \ge 3 ≥3 :划分方案是唯一的,连续段要么是整个排列要么在每段内部。其中段数为 3 3 3 无法构造,为 4 4 4 及以上可以构造。
- 段数 = 2 =2 =2 :不妨假设 1 1 1 在 n n n 前面。如果长度为 i i i 的前缀正好是 [ 1 , i ] [1,i] [1,i] 的排列,那么划开 i i i 和 i + 1 i+1 i+1 。连续段要么是若干个整段,要么是段内部。
所以 f ( n ) = ∑ k ≥ 2 ∏ ∑ i = 1 k a i = n f ( a i ) + ∑ k ≥ 4 ∏ ∑ i = 1 k a i = n f ( a i ) f(n) = \sum_{k\ge 2} \prod_{\sum_{i=1}^k a_i = n} f(a_i) + \sum_{k\ge 4} \prod_{\sum_{i=1}^k a_i = n} f(a_i) f(n)=∑k≥2∏∑i=1kai=nf(ai)+∑k≥4∏∑i=1kai=nf(ai)
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, md;
cin >> n >> md;
auto add = [&](int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
};
auto mul = [&](int x, int y) {
return (long long) x * y % md;
};
vector<vector<int>> sum(4, vector<int>(n + 1));
vector<int> dp(n + 1);
dp[1] = sum[0][1] = 1;
for (int i = 2; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
for (int k = 3; k; --k) {
add(sum[k][i], mul(dp[j], sum[k - 1][i - j]));
if (k == 3) {
add(sum[k][i], mul(dp[j], sum[k][i - j]));
}
}
}
dp[i] = sum[3][i];
for (int k = 3; k; --k) {
add(dp[i], sum[k][i]);
}
sum[0][i] = dp[i];
}
for (int i = 1; i <= n; ++i) {
cout << dp[i] << "\n";
}
return 0;
}
Heretical … Möbius
注意到 4 , 9 , 25 , 49 4, 9, 25, 49 4,9,25,49 的倍数的 μ \mu μ 一定为 0 0 0 ,所以枚举余数爆搜即可。对于一个合法的序列要搜索的东西不多,加个卡时判无解即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 200;
const int MAX = 1e9;
const int SQ = sqrt(MAX);
int main() {
#ifdef wxh010910