• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

RomanLin

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【区间合并+贡献法】codeforces 1789 C. Serval and Toxel's Arrays

题目

https://codeforces.com/problemset/problem/1789/C

题意

第一行输入一个正整数 \(T(1 \leq T \leq 10^4)\),代表 \(T\) 组测试用例。

对于每组测试用例:第一行输入两个正整数 \(n, m(1 \leq n, m \leq 2 \times 10^5)\),分别代表要输入的数组长度和修改次数。第二行输入一个长度为 \(n\) 的数组 \(a(1 \leq a[i] \leq n + m)\)。接下来 \(m\) 行,每行输入两个整数 \(p, q\),代表将 \(a[p]\) 的值修改为 \(q\)。

保证初始数组,以及每次修改后的数组,都不包含重复元素,且对数组的修改是永久操作;\(n, m\) 的总和都不超过 \(2 \times 10^5\)。

经过 \(m\) 次修改,我们可以得到如下 \((m + 1)\) 个数组:
\(A_1 = a\)
\(A_2 = 第一次修改后的 a\)
...
\(A_{m+1} = 第 m 次修改后的 a\)

要求计算出这个 \((m + 1)\) 个数组两两数组进行组合,不同元素的数量之和。

题解

不妨假设有 \(m + 1\) 组数组,这 \(m + 1\) 组数组中元素 \(x\) 的出现次数为 \(u(1 \leq u \leq m + 1)\) 次,那么这个 \(x\) 对于答案的贡献会是多少呢?

分类讨论:

  1. 与含有元素 \(x\) 的其他一组数组进行组合,那么两两组合 \(x\) 只会贡献 \(1\) 次答案;若总的有 \(u\) 组数组具有元素 \(x\),则只会贡献 \((u \times (u - 1) >> 1)\) 次答案;
  2. 与不含有元素 \(x\) 的其他数组进行组合,不妨假设其他数组共有 \(v\) 组,含有元素 \(x\) 的数组共有 \(u\) 组,那么会贡献 \((u * v)\) 次答案,其中 \(u + v = m + 1\)。

综上所述,我们只需要维护出每个元素出现的区间(不妨用左闭右开区间),在最后进行区间合并,得到每个元素在全部数组中出现的次数,不妨假设次数为 \(u\),则其对答案的贡献即为:\((u \times (u - 1) >> 1) + u \times (m + 1 - u)\)。

参考代码

#include<bits/stdc++.h>
#define PII pair<int, int>
#define eb(x) emplace_back(x)
using namespace std;

typedef long long ll;

constexpr int N = 2e5 + 7;
int T, n, m, p, q;
int a[N];

void solve() {
	ll ans = 0LL;
	cin >> n >> m;
	int w = m + 1;
	vector<PII> v[n + m + 1];
	for (int i = 1; i <= n; ++ i) {
		cin >> a[i];
		v[a[i]].eb(PII(0, w));
	}
	for (int i = 1; i <= m; ++ i) {
		cin >> p >> q;
		v[a[p]].back().second = i;
		v[q].eb(PII(i, w));
		a[p] = q;
	}
	for (int i = 1; i <= n + m; ++ i) {
		if (v[i].empty()) continue;
		ll len = 0LL;
		int l = v[i][0].first, r = v[i][0].second;
		for (int j = 1; j < v[i].size(); ++ j) {
			if (v[i][j].first == r) r = v[i][j].second;
			else {
				len += r - l;
				l = v[i][j].first;
				r = v[i][j].second;
			}
		}
		len += r - l;
		ans += (len * (len - 1) >> 1) + len * (w - len);
	}
	cout << ans << '\n';
}

int main() {
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	cin >> T;
	while (T --) {
		solve();
	}
	return 0;
}

posted on 2025-01-09 22:41  RomanLin  阅读(43)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3