acm -(并查集、启发式合并、gcd、枚举因子)2020 China Collegiate Programming Contest Changchun Onsite K. Ragdoll

题面
传送门
本题考虑直接对每个 i i i求出所有满足 i j = g c d ( i , j ) i^j=gcd(i,j) ij=gcd(i,j) j j j,然后存在 g g g数组中,对于查询修改操作维护一个并查集即可,合并的时候采用启发式合并(小的往大的上合)

核心问题在于如何初始化 g g g数组,考虑到每个数的因子其实不多,不妨枚举因子即可,假设枚举的因子为 d d d a = k d a=kd a=kd,那么 b = a   x o r   d b=a\,xor\,d b=axord,然后检验一下是否满足 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=d即可,预处理复杂度为 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

int fa[maxn],a[maxn],sz[maxn];
vector<int>g[maxm];
unordered_map<int,int>cnt[maxn];
ll ans=0;
int fd(int rt){
	return rt==fa[rt]?rt:(fa[rt]=fd(fa[rt]));
}
int main(){
	FOR(i,1,maxm){
		for(register int j=i;j<maxm;j+=i){
			if((j^i)>=maxm || !(j^i) || (j^i)>=maxm)continue;
			if(__gcd(j,j^i)==i){
				g[j].push_back(j^i);
			}
		}
	}
	int n=rd(),m=rd();
	FOR(i,1,n+1){
		a[i]=rd();
		fa[i]=i;
		cnt[i][a[i]]++;
		sz[i]=1;
	}
	while(m--){
		int t=rd();
		if(t==1){
			int x=rd(),v=rd();
			fa[x]=x;
			cnt[x][v]++;
			sz[x]=1;
			a[x]=v;
		}else if(t==2){
			int x=rd(),y=rd();
			int fx=fd(x),fy=fd(y);
			if(fx!=fy){
                if(sz[fx]>sz[fy])swap(fx,fy);
                unordered_map<int,int>::iterator it1;
                for(it1=cnt[fx].begin();it1!=cnt[fx].end();++it1){
                    FOR(i,0,g[it1->first].size()){
                        if(cnt[fy].count(g[it1->first][i]))ans+=1ll*it1->second*cnt[fy][g[it1->first][i]];
                    }
                }
                for(it1=cnt[fx].begin();it1!=cnt[fx].end();++it1)cnt[fy][it1->first]+=it1->second;
                cnt[fx].clear();
                sz[fy]+=sz[fx];
                fa[fx]=fy;
			}

		}else{
			int x=rd(),v=rd();
			int fx=fd(x);
			FOR(i,0,g[a[x]].size()){
				ans-=cnt[fx][g[a[x]][i]];
			}
			cnt[fx][a[x]]--;
			a[x]=v;
			FOR(i,0,g[v].size()){
				ans+=cnt[fx][g[v][i]];
			}
			cnt[fx][v]++;
		}
		printf("%lld\n",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值