C. Serval and Toxel‘s Arrays(数学贡献法)

Problem - C - Codeforces

Toxel喜欢数组。在前往帕尔迪亚地区之前,塞瓦尔给了他一个阵列作为礼物。这个数组有n个成对不同的元素。为了获得更多的数组,Toxel对初始数组进行了m次操作。在第i个操作中,他将第(i - 1)-th数组的第p个元素修改为v,得到第i个数组(初始数组a编号为0)。在修改过程中,Toxel保证每个数组的元素在每次操作后仍然是成对不同的。最后,Toxel得到m +1个数组,记为Ao = a, A1,…,我。对于每一对(2,j) (0 <i <j< m), Toxel将其值定义为Ai和Aj的级联中不同元素的数量。现在Toxel想知道,所有对的值的和是多少?请帮他算一下答案。输入每个测试包含多个测试用例。第一行包含测试用例的数量t (1 < t < 104)。测试用例的描述是流畅的。每个测试用例的第一行包含两个整数n和m (1 < n, m < 2-105)——数组的长度和操作的数量。每个测试用例的第二行包含n个整数a1, a2,…, an (1 < a < n + m)。保证所有a,是成对不同的。每个测试用例下m行的每一行都包含两个整数p;v (1 <pi Sn, 1 <o Sn + m)-被修改的元素的位置和它的新值。它保证每个数组的元素在每次修改后仍然是成对不同的。保证所有测试用例的n和m之和不超过2- 105。输出对于每个测试用例,打印一个整数——所有数组对的值的和。

Example

input

Copy

 
   

3

3 2

1 2 3

1 4

2 5

1 1

1

1 1

10 10

4 6 9 12 16 20 2 10 19 7

1 3

5 4

2 17

2 18

6 11

7 1

8 17

5 5

5 5

2 2

output

Copy

13
1
705

请注意在第一个测试用例中,数组的变化如下:[1,2,3][4,2,3][4,5,3]。第0个数组和第1个数组的连接为[1,2,3,4,2,]。有4个不同的元素。第0个数组和第2个数组的连接为[1,2,3,4,5,3]。有5个不同的元素。1-st数组和2-nd数组的连接为[4,2,3,A,5,]。有4个不同的元素。删除线元素是数组中的重复元素。因此,答案是4+5+4= 13。在第二个测试用例中,注意数组在操作之后可能保持不变。

题解:

题中保证无论如何更新,数组内都不会出现相等的数,也就是对于某个数字在一个数组内最多出现一次,只需要统计一下这个数出现的次数就可以知道这个数在多少个数组内

假设一个数出现在数组中次数为m次,总数组数为n

包含一个数

1.与其他数组中有相同的数,

m*(n-m)对答案贡献

2.没有与其他数组有相同的数

总贡献为 +m*(n-m)

 关于统计次数,我们发现一个数的出现次数是一段一段的,所以我们开一个数组记录上一次更新的位置,每次另一起一段的时候更新位置,并把旧的一段统计如数组即可。注意,最后残余的要清理干净。

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
#include<map>
#include<queue>
using namespace std;
#define int long long
const int N = 6e5 + 10;
int cnt[N];
int a[N];
int st[N];
void solve()
{
	int n,m;
	cin >> n >> m;
	for(int i = 1;i <= n+m;i++)
	{
		cnt[i] = 0;
		st[i] = 0;
	}
	for(int i = 1;i <= n;i++)
	{
		cin >> a[i]; 
	}
	for(int i = 1;i <= m;i++)
	{
		int p,v;
		cin >> p >> v;
		if(a[p] != v)
		{
			cnt[a[p]] += i - st[p];//i - st[p]上个数的出现的次数,st[p]是上一次数出现的位置
			st[p] = i;
		}
		a[p] = v;
	}
	for(int i = 1;i <= n;i++)
	{
		cnt[a[i]] += m - st[i] + 1;//最终更新到最后是没有更新过,也没有记录的,所以要记录上
	}
	int ans = 0;
	for(int i = 1;i <= n + m;i++)
	{
		ans += cnt[i]*(m + 1 - cnt[i]);
		ans += cnt[i]*(cnt[i] - 1)/2;
	}
	cout << ans <<"\n";
}
signed main()
{
	int t = 1;
	cin >> t;
	while(t--)
	{
		solve();
	} 
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值