题目大意:有一长度为n的字符串a,有m次操作,每次操作将a中位置为x的数修改成y,从而产生一个新的数组,产生了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;
}