翼人的诅咒早就在第一千个夏天被打破了,所以化作名为 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;
}