\(\color{black}\texttt{A. 序列}\)
題目描述
給定 \(N\) 個數,每個數均可寫成 \(pq(p,q\in\mathbb{P},p<q)\) 的形式,問最長能找到多長的子序列使得任意相鄰兩項 \(x_i=p_1q_1,x_{i+1}=p_2q_2(p_1,q_1,p_2,q_2\in\mathbb{P},p_1<q_1,p_2<q_2)\) 滿足 \(q_1=p_2\) ?
思路
按照 \(p\) 排序並 dp 即可。
程式碼
#include<bits/stdc++.h>
using namespace std;
using pii = pair<int, int>;
const int MAXN = 50001;
struct Num {
int p, q;
}a[MAXN];
int n, Max[MAXN], ans;
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1, x; i <= n; ++i) {
cin >> x;
for(int j = 2; j * j <= x; ++j) {
if(x % j == 0) {
a[i] = {j, x / j};
break;
}
}
}
sort(a + 1, a + n + 1, [](const Num &x, const Num &y) { return x.p < y.p || (x.p == y.p && x.q < y.q); });
for(int i = 1; i <= n; ++i) {
ans = max(ans, Max[a[i].p] + 1);
Max[a[i].q] = max(Max[a[i].q], Max[a[i].p] + 1);
}
cout << ans;
return 0;
}
\(\color{black}\texttt{B. 生成最小樹}\)
題目描述
給定一張圖和其中的一棵樹,每次操作可以將一條邊的邊權 \(-1\),求最少需要多少次操作才能使這棵樹變成最小生成樹?
思路
因為每一條非樹邊必須 \(\ge\) 邊兩個端點樹上路徑的每一條邊,不然這條邊一定比樹邊更優,所以使用樹鏈剖分求出每條邊的限制即可。
程式碼
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
const int MAXN = 10001, MAXM = 100001;
struct Edge {
int u, v, w;
}g[MAXM];
struct Segment_Tree {
int l[4 * MAXN], r[4 * MAXN], dfn[MAXN], W[MAXN], Min[4 * MAXN], lazy[4 * MAXN];
void build(int u, int s, int t) {
l[u] = s, r[u] = t, lazy[u] = INT_MAX;
if(s == t) {
Min[u] = W[dfn[s]];
return;
}
int mid = (s + t) >> 1;
build(2 * u, s, mid), build(2 * u + 1, mid + 1, t);
Min[u] = min(Min[2 * u], Min[2 * u + 1]);
}
void tag(int u, int x) {
Min[u] = min(Min[u], x), lazy[u] = min(lazy[u], x);
}
void pushdown(int u) {
tag(2 * u, lazy[u]), tag(2 * u + 1, lazy[u]), lazy[u] = INT_MAX;
}
void update(int u, int s, int t, int x) {
if(l[u] >= s && r[u] <= t) {
tag(u, x);
return;
}
pushdown(u);
if(s <= r[2 * u]) {
update(2 * u, s, t, x);
}
if(t >= l[2 * u + 1]) {
update(2 * u + 1, s, t, x);
}
Min[u] = min(Min[2 * u], Min[2 * u + 1]);
}
int Get(int u, int p) {
if(l[u] == r[u]) {
return Min[u];
}
pushdown(u);
return (p <= r[2 * u] ? Get(2 * u, p) : Get(2 * u + 1, p));
}
}tr;
int n, m, sz[MAXN], f[MAXN], son[MAXN], dfn[MAXN], tot, top[MAXN];
ll ans;
vector<pii> e[MAXN];
map<pii, bool> vis;
map<pii, int> _W;
void dfs(int u, int fa) {
f[u] = fa, sz[u] = 1;
for(auto [v, w] : e[u]) {
if(v != fa) {
tr.W[v] = w;
dfs(v, u);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) {
son[u] = v;
}
}
}
}
void DFS(int u, int fa) {
dfn[u] = ++tot, tr.dfn[tot] = u;
if(son[u]) {
top[son[u]] = top[u], DFS(son[u], u);
}
for(auto [v, w] : e[u]) {
if(v != fa && v != son[u]) {
top[v] = v, DFS(v, u);
}
}
}
void changepath(int u, int v, int w) {
for(; top[u] != top[v]; ) {
if(dfn[u] > dfn[v]) {
tr.update(1, dfn[top[u]], dfn[u], w);
u = f[top[u]];
}else {
tr.update(1, dfn[top[v]], dfn[v], w);
v = f[top[v]];
}
}
if(min(dfn[u], dfn[v]) + 1 <= max(dfn[u], dfn[v])) {
tr.update(1, min(dfn[u], dfn[v]) + 1, max(dfn[u], dfn[v]), w);
}
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; ++i) {
cin >> g[i].u >> g[i].v >> g[i].w;
_W[{g[i].u, g[i].v}] = _W[{g[i].v, g[i].u}] = g[i].w;
}
for(int i = 1, u, v; i < n; ++i) {
cin >> u >> v;
e[u].push_back({v, _W[{u, v}]});
e[v].push_back({u, _W[{u, v}]});
vis[{u, v}] = vis[{v, u}] = 1;
}
dfs(1, 0);
top[1] = 1;
DFS(1, 0);
tr.build(1, 1, n);
for(int i = 1; i <= m; ++i) {
if(!vis.count({g[i].u, g[i].v})) {
changepath(g[i].u, g[i].v, g[i].w);
}
}
for(int i = 2; i <= n; ++i) {
ans += max(0, _W[{f[i], i}] - tr.Get(1, dfn[i]));
}
cout << ans;
return 0;
}
\(\color{black}\texttt{C. 互質序列}\)
題目描述
給定 \(l,r\),求有多少個序列滿足以下條件:
- 序列單調遞增。
- 序列中的數 \(\in[l,r]\)。
- 序列中的數兩兩互質。
思路
因為 \(r-l+1\le 100\),所以出現超過兩次的質數一定 \(\le 100\),所以狀壓 dp 即可。
但是這樣可能會 \(\texttt{TLE}\),所以要先預處理出所有出現超過一次的質數。
令 \(V=25\),空間複雜度 \(O(2^V)\),時間複雜度 \(O((r-l)2^V)\)。
程式碼
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int prime[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97};
ll a, b, dp[1 << 25], ans;
vector<int> v;
void FileIO(const string &s) {
freopen((s + ".in").c_str(), "r", stdin);
freopen((s + ".out").c_str(), "w", stdout);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
FileIO("rlprime");
cin >> a >> b;
for(int i = 1; i <= 25; ++i) {
int cnt = 0;
for(ll j = a; j <= b; ++j) {
cnt += (j % prime[i] == 0);
}
if(cnt > 1) {
v.push_back(prime[i]);
}
}
dp[0] = 1;
for(ll i = a; i <= b; ++i) {
int res = 0;
for(int j = 0; j < int(v.size()); ++j) {
if(i % v[j] == 0) {
res |= (1 << j);
}
}
for(int j = (1 << int(v.size())) - 1; j >= 0; --j) {
if(!(j & res)) {
dp[j | res] += dp[j];
}
}
}
for(int i = 0; i < (1 << int(v.size())); ++i) {
ans += dp[i];
}
cout << ans - 1;
return 0;
}