C AIR Triplet (牛客练习赛98)

这篇博客讨论了一个关于数组和位运算的算法问题,即如何在每次修改数组元素时,快速计算满足特定异或条件的3-AIR三元组数量。作者提出利用Map存储每个元素的计数,并通过组合公式动态更新组数,解决了效率问题。文章还给出了C++代码实现,展示了如何处理单点修改并输出结果。
摘要由CSDN通过智能技术生成

翼人的诅咒早就在第一千个夏天被打破了,所以化作名为 Air 的乌鸦的往人就不需要再想这个问题了,开始致力于解决困扰人们多年的 3-SUM 问题。但是 3-SUM 问题很困难,所以他希望能解决 3-AIR 问题。

小 L 定义对于三元组 (i,j,k)(i,j,k),如果其满足 1\le i<j<k\le n1≤i<j<k≤n 且 a_i\oplus a_j=0,a_j\oplus a_k=0ai​⊕aj​=0,aj​⊕ak​=0 (其中 \oplus⊕ 为按位异或),那么我们称其为 3-AIR 三元组。你除了要求出有多少 3-AIR 三元组,还需要面临一些单点修改。

具体而言,您面临的问题是:现在有一个长 nn 的序列 aa,有 mm 次操作,每次操作给定 x,yx,y,你需要将 a_xax​ 修改为 yy,然后对于每次修改输出修改完的数列中,有多少对 (i,j,k)(i,j,k) 满足 1\le i<j<k\le n1≤i<j<k≤n 且 a_i\oplus a_j=0,a_j\oplus a_k=0ai​⊕aj​=0,aj​⊕ak​=0,其中 \oplus⊕ 为按位异或。

题目的意思是在每次修改的时候找一下不同下标但是数相同的三个数有几组。我们可以先用map存他每个数的个数,然后遍历一遍map,算出来有几组,相当于在大于等于3个相同的数里选三个,那么根据组合的知识,C(m,n)=(n!(n-1)!...(n-m+1)!)/m!,那么注意如果我们每次修改就要从头算一遍的话会超时,所以我们可以在输入的时候把总共的算一遍(这个时候注意如果一个数在数组中出现的个数小于3的话就是0,因为我们要找3个相同的数一组),然后再每次输入xy的时候将总数中的ax和y的组数删掉,修改一下ax和y的数量之后再算一下加上,然后注意把ax修改成y就可以了

#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
int const N=200005;
int n,m;
int a[N];

typedef long long ll;
ll C(int u){//算出来C(3,u)
	ll q=1;
	for(int i=1,j=u;i<=3;i++,j--){
		q*=j;
	}
	return max(0ll,q/6);//注意要除以3!而且当u<3的时候是负数,我们要避免这种情况和0取max就可
}
int main(){
	cin>>n>>m;
	map<int,int> mp;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		mp[a[i]]++;
	}
	ll ans=0;
	for(auto it:mp){
		ans+=C(it.second);//算出来总的组数
	}
	while(m--){
		int x,y;
		cin>>x>>y;
		ans=ans-C(mp[a[x]])-C(mp[y]);//先减去要修改的数的组数
		mp[a[x]]--;
		mp[y]++;
		ans=ans+C(mp[a[x]])+C(mp[y]);//再加上修改之后的组数
		a[x]=y;//修改
		cout<<ans<<endl;
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值