逐月信息学 2024 提高组 #2

\(\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;

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  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;
}

\(\color{black}\texttt{D. 鬼鬼的序列}\)

题目描述

给定序列 \(A\),选出一个最长的区间 \([l,r]\),使得能在里面插入不超过 \(k\) 个数并排序后变为一个公差为 \(d\) 的等差数列。如果有多个长度相同的区间,选择 \(l\) 最小的。

思路

首先对 \(a_i % d\) 进行分类,设 \([l,r]\) 是选出的一个极大区间。使用两个单调栈维护 \([l,r]\)

代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXN = 200001;
const ll INF = (ll)(1e12);

struct Segment_Tree {
  int l[4 * MAXN], r[4 * MAXN];
  ll Min[4 * MAXN], lazy[4 * MAXN];
  void build(int u, int s, int t) {
    l[u] = s, r[u] = t, lazy[u] = 0;
    if(s == t) {
      Min[u] = 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, ll x) {
    Min[u] += x, lazy[u] += x;
  }
  void pushdown(int u) {
    tag(2 * u, lazy[u]), tag(2 * u + 1, lazy[u]), lazy[u] = 0;
  }
  void update(int u, int s, int t, ll 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]);
  }
  ll Get(int u, int s, int t, ll x) {
    if(r[u] < s || l[u] > t || Min[u] > x) {
      return t + 1;
    }
    if(l[u] >= s && r[u] <= t) {
      if(l[u] == r[u]) {
        return l[u];
      }
      return (Min[2 * u] <= x ? Get(2 * u, s, t, x) : Get(2 * u + 1, s, t, x));
    }
    int v = Get(2 * u, s, t, x);
    return (v <= t ? v : Get(2 * u + 1, s, t, x));
  }
}tr;

int n, k, d, a[MAXN], pos, ans, top[2], stk[2][MAXN];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> k >> d;
  for(int i = 1; i <= n; ++i) {
    cin >> a[i];
  }
  if(!d) {
    for(int i = 1, j = 1; i <= n; i = j) {
      for(; j <= n && a[j] == a[i]; ++j) {
      }
      if(j - i > ans) {
        ans = j - i, pos = i;
      }
    }
    cout << pos << " " << pos + ans - 1;
    return 0;
  }
  tr.build(1, 1, n);
  for(int l = 1, r = 1; l <= n; l = r) {
    for(; r <= n && (a[r] % d + d) % d == (a[l] % d + d) % d; ++r) {
    }
    map<int, int> mp;
    top[0] = top[1] = 0;
    for(int i = l; i < r; ++i) {
      if(mp.count(a[i])) {
        tr.update(1, 1, mp[a[i]], INF);
      }
      mp[a[i]] = i;
      for(; top[0] && a[stk[0][top[0]]] / d >= a[i] / d; --top[0]) {
        tr.update(1, stk[0][top[0] - 1] + 1, stk[0][top[0]], a[stk[0][top[0]]] / d - a[i] / d);
      }
      for(; top[1] && a[stk[1][top[1]]] / d <= a[i] / d; --top[1]) {
        tr.update(1, stk[1][top[1] - 1] + 1, stk[1][top[1]], a[i] / d - a[stk[1][top[1]]] / d);
      }
      stk[0][++top[0]] = stk[1][++top[1]] = i;
      tr.update(1, i, i, a[stk[1][1]] / d - a[stk[0][1]] / d);
      int x = tr.Get(1, l, i, i + k);
      if(i - x + 1 > ans) {
        ans = i - x + 1, pos = x;
      }
    }
  }
  cout << pos << " " << pos + ans - 1;
  return 0;
}
posted @ 2024-07-05 22:06  Yaosicheng124  阅读(15)  评论(0编辑  收藏  举报