20250823

T1

看到第一眼,合并芒果?不对,这是 \(a \times b\),而且题目中还有取模,哪里有求最小值,还要取模的?最小值不会变么?

这启示我们,也许怎么合并都是一样的。手模/打一个暴力,你会发现确实是这样,那么第一问就好解决了,第二问怎么做?

很简单,每一次合并芒果时都会在总数里选两个芒果合并,这个直接乘法原理组合数计算就行了。

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int mod = 1e9 + 7, kN = 1e5 + 7;

int T, n, op, ans1, a[kN], ans2;

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  for (cin >> T; T--;) {
    cin >> n >> op, ans1 = 0, ans2 = 1;
    if (n == 1) ans2 = 0;
    fill(a + 1, a + (n << 1) + 3, 0);
    for (int i = 1; i <= n; i++) {
      cin >> a[i];
      if (i - 1) (ans2 *= 1ll * i * (i - 1) / 2 % mod) %= mod;
    }
    for (int i = 1, r = n; i <= r; i += 2, r++) {
      if (a[i] == 0 || a[i + 1] == 0) break;
      a[r + 1] = (a[i] + a[i + 1]) % mod, (ans1 += a[i] * a[i + 1] % mod) %= mod;
    }
    cout << ans1 << ' ' << op * ans2 << '\n';
  }

  return 0;
}

T2

看了很久 T3,没时间写了。

我们发现亲密度越大时,对数一定越多。那么这就可以二分出小于 \(k\) 对的最大亲密值。

check 可以写二分,也可以写双指针,首先不考虑所有相同颜色的人,假设现在的亲密值需要小于等于 \(x\),那么就相当于一个长度为 \(x\) 的滑动窗口,在长度为 \(n\) 的序列中滑动,方案数是 \(n \times (n - x)\),并且在最后还要加上 \(x\) 之中的对数,是 \(\dfrac{x(x-1)}{2}\),最后处理所有相同颜色的,这个可以将所有相同颜色的人放进同一个 vector 中,然后由于下标有单调性,双指针处理。

最后我们得到了一个等于 \(k\) 的最大亲密值,扫一遍所有亲密值为最大 \(x\) 的即可。

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kN = 1e5 + 7;

int n, k, a[kN], mx = 0, ans, p;
vector<int> g[kN];

// 在亲密度为 <= x 时有几对
int calc(int x) {
  int res = x * (n - x) + x * (x - 1) / 2;
  for (int i = 1; i <= n; i++) {
    int m = g[i].size();
    for (int l = 0, r = 0; r < m; r++) {
      while (l < m && g[i][r] - g[i][l] > x) l++;
      res -= r - l;
    }
  }
  return res;
}

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n >> k;
  for (int i = 1; i <= n; i++) {
    cin >> a[i], g[a[i]].push_back(i);
  }
  // 对数严格小于 k 的最大亲密度.
  if (calc(n) < k) return cout << -1, 0;
  for (int l = 1, r = n - 1, mid; l <= r;) {
    mid = (l + r) >> 1;
    if (calc(mid) < k)
      l = mid + 1, mx = mid;
    else
      r = mid - 1;
  }
  ans = calc(mx), mx++;
  for (int i = 1; i <= n - mx; i++) {
    if (a[i] != a[i + mx]) ans++;
    if (ans == k) return cout << i << ' ' << i + mx, 0;
  }

  return 0;
}

T3

看了好久没看出来

两个定理:

唯一分解定理:\(\begin{aligned}n=\prod^k_{i=1}\end{aligned}a_i^{p_i}\),正约数和定理:\(\begin{aligned}S=\prod^k_{i=1}\sum^{p_i}_{j=0}a_i^j\end{aligned}\)

由此可以通过拆分数 \(n\) 做,每一次除去一个数 \(a_i^{q_i}\),此时\(0 \le q_i \le p_i\),然后将 \(sum\) 乘上 \(\sum_{j=0}^{q_j}a_i^j\)。最后如果拆完了,或者剩下一个极大质因子,那么答案加 \(1\),但是注意需要去重,见 \(n=684\),有可能最后拆出不同方案,因此需要保证这个极大质因数大于 \(\sqrt{2\times 10^9}\)

这个过程可以爆搜,看似不正确,实则因为质因数个数极少,可以通过。

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int kN = 1e6 + 7;

int T, n, g[kN], tot;
bool b[kN];
vector<int> r;

void Prime() {
  for (int i = 2; i < kN; i++) {
    if (b[i] == 0) g[++tot] = i;
    for (int j = 1; j <= tot; j++) {
      if (i * g[j] >= kN) break;
      b[i * g[j]] = 1;
      if (i % g[j] == 0) break;
    }
  }
}

bool isp(int x) {
  if (x <= 1) return 0;
  for (int i = 2; i * i <= x; i++) {
    if (x % i == 0) return 0;
  }
  return 1;
}

void DFS(int now, int x, int sum) {
  if (now == 1) return r.push_back(sum);
  if (isp(now - 1) && now > g[x]) r.push_back(sum * (now - 1));
  for (int i = x; g[i] * g[i] <= now; i++) {
    for (int j = g[i] + 1, val = g[i]; j <= now; val *= g[i], j += val) {
      if (now % j == 0) DFS(now / j, i + 1, sum * val);
    }
  }
}

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  Prime();
  for (cin >> T; T--;) {
    cin >> n, r.clear(), DFS(n, 1, 1);
    sort(r.begin(), r.end());
    cout << r.size() << '\n';
    for (int x : r) cout << x << ' ';
    if (r.size()) cout << '\n';
  }

  return 0;
}

T4

我不会 FHQ 啊!所以我打了个 \(O(n^2)\) 的暴力水过去了!

设在前 \(i\) 位上升子序列中 \(a_i\) 的最小值为 \(f_i\),当 \(f_{i-1} < l\) 时,有 \(f_i = \min\{l,f_i\}\),当 \(l \le f_{i-1} < r\) 时,有 \(f_i = f_{i-1} + 1\),当 \(f_{i-1} \ge r\) 时,不可能转移。

\(f_0=0\),其他都为极大值,最后答案就是在所有不为极大值的 \(f_i\) 中取最大。

这个显然是 \(O(n^2)\) 的,由于有单调性,上面的转移形式可以用平衡树维护,但我不会啊,我直接卡时就过了。

#include <bits/stdc++.h>

using namespace std;

const int kN = 3e5 + 7;

int n, l[kN], r[kN], f[kN], ans;

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> n;
  for (int i = 1; i <= n; i++) cin >> l[i] >> r[i];
  memset(f, 0x7f, sizeof f), f[0] = 0;
  int s = 0;
  for (int i = 1; i <= n; i++) {
    s = 0;
    for (int j = i; j; j--) {
      s++;
      if (f[j - 1] < l[i]) f[j] = min(f[j], l[i]);
      if (l[i] <= f[j - 1] && f[j - 1] < r[i]) f[j] = min(f[j], f[j - 1] + 1);
      if (s * n > 8e7) break;
    }
  }
  for (int i = 1; i <= n; i++) {
    ans += (f[i] < 1e9);
  }
  cout << ans;

  return 0;
}
posted @ 2025-08-23 11:13  PikachuQAQ  阅读(5)  评论(0)    收藏  举报