C. Serval and Toxel‘s Arrays codeforces1789C

Problem - C - Codeforces

题目大意:有一长度为n的字符串a,有m次操作,每次操作将a中位置为x的数修改成y,从而产生一个新的数组A_{i},产生了m个数组后,将这m个数组和原数组a,两两融合并去重,产生m*(m+1)/2个数组,求这些数组中元素数量之和

1<=n,m<=2e5;1<=ai<=n+m

思路:可以发现如果一个数出现在某个Ai里出现,那么只有它与其他同样拥有这个数的Ai融合时才会出现贡献重复,需要排除一半的贡献,所以每个数字的贡献就是总的融合数组数量C(2,m+1)-没有贡献的融合数组数量A(2,该数在m+1和数组中出现的次数)/2,然后我们要解决的问题就是统计1到n+m的每个数在m+1个数组中出现的次数

因为在任意的数组中都没有重复的元素,所以我们记录每个数字上次出现的位置,a中的所有数都标为0,没出现的为-1,然后每次修改时,cnt[a[x]]+=当前修改次数i-上次出现位置pos[a[x]],并将pos[a[x]]置为-1,pos[y]=i,然后所有m次操作结束时,对于a数组中最后剩余的数cnt[a[i]]+=m+1-pos[a[i]],最后对于1到m+n的每个数字按之前推导的公式结果求和即可

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5 + 5;
typedef long long ll;
ll app[N];
ll a[N];
ll cnt[N];
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		ll n, m;
		cin >> n >> m;
		for (int i = 1; i <= n + m; i++)
		{//对所有数字进行初始化
			app[i] = -1;
			cnt[i] = 0;
		}
		for (int i = 1; i <= n; i++)
		{
			cin >> a[i];
			app[a[i]] = 0;//a数组中的数的出现位置记为0
		}
		for (int i = 1; i <= m; i++)
		{
			int x, y;
			cin >> x >> y;
			cnt[a[x]] += i - app[a[x]];//a[x]在被修改前出现在了几个数组里
			app[a[x]] = -1;//重置a[x]出现位置
			a[x] = y;
			app[y] = i;//因为会有重复操作,y的操作必须在x之后
		}
		for (int i = 1; i <= n; i++)
		{//记录当前a数组中剩余数的出现次数
			if (app[a[i]] != -1)
			{
				cnt[a[i]] += m + 1 - app[a[i]];
			}
		}
		ll ans = 0;
		for (int i = 1; i <= n + m; i++)
		{//对所有数统计答案
			ans += m * (m + 1) / 2 - (m - cnt[i]) * (m - cnt[i] + 1) / 2;
		}
		cout << ans << endl;
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值