2023大厂真题提交网址(含题解):
www.CodeFun2000.com(http://101.43.147.120/)
最近我们一直在将收集到的机试真题制作数据并搬运到自己的OJ上,供大家免费练习,体会真题难度。现在OJ已录入50+道2023年最新大厂真题,同时在不断的更新。同时,可以关注"塔子哥学算法"公众号获得每道题的题解。
题目大意:
直接题意:给你n个点,带点权
a
i
a_i
ai。操作m次,模拟并查集的合并过程,每次操作之后询问每个集合中满足
g
c
d
(
a
i
,
a
j
)
=
a
i
⊕
a
j
gcd(a_i,a_j)=a_i \oplus a_j
gcd(ai,aj)=ai⊕aj的点对个数.还有对点权的修改.
n
,
m
≤
1
e
5
n,m \leq 1e5
n,m≤1e5
题目思路:
其实这题很显然。让我们维护并查集内部的信息,无非就是线段树合并或者启发式合并set/map嘛.复杂度都是 O ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn),稍微说一下我分析 g c d ( a i , a j ) = a i ⊕ a j gcd(a_i,a_j)=a_i \oplus a_j gcd(ai,aj)=ai⊕aj的思路.
观察式子发现异或实在没有什么规律,但是 g c d gcd gcd还是很有规律的。
我们知道,对于一个确定的 x x x。枚举他的 g c d gcd gcd不同的取值本质上就是枚举他的约数的个数。而枚举 1 到 n 1到n 1到n的约数 就是一个调和级数的复杂度 O ( n l n n ) O(nlnn) O(nlnn)
对于 x x x,枚举其约数 d d d.那么上式 => d = x ⊕ r e s d =x \oplus res d=x⊕res 推出 r e s = d ⊕ x res=d \oplus x res=d⊕x.然后再判断 g c d ( r e s , x ) 是否等于 d gcd(res,x)是否等于d gcd(res,x)是否等于d.等于的话,那 r e s res res就是x的一个解,记录下来.
而且对于每个 g c d gcd gcd, x x x最多就存在一个合法的值.所以这么做一定是对的。
然后map存集合的值。合并的时候暴力算贡献即可。具体看代码。
另外,对点权的修改:另开数组模拟即可。
AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
vector<int> fact[maxn * 2];
void init ()
{
for (int i = 1 ; i < maxn * 2 ; i++)
for (int j = i; j < maxn * 2; j += i){
if (i == j) continue;
if (__gcd(i , j^i) == i) fact[j].push_back(j^i);
}
}
int f[maxn * 3] , sz[maxn * 3];
ll res;
unordered_map <int,int> q[maxn * 3];
int getf (int x) {
return x == f[x] ? x : f[x] = getf(f[x]);
}
void mer (int a , int b)
{
int fa = getf(a) , fb = getf(b);
if (fa == fb) return ;
if (sz[fa] < sz[fb]){
f[fa] = fb;
for (auto & g : q[fa]){
for (auto &k : fact[g.first]) {
if (q[fb].find(k) != q[fb].end()){
res += 1ll * q[fb][k] * g.second;
}
}
}
for (auto & g : q[fa]){
q[fb][g.first] += g.second;
}
q[fa].clear();
sz[fb] += sz[fa];
}
else {
f[fb] = fa;
for (auto & g : q[fb]){
for (auto &k : fact[g.first]) {
if (q[fa].find(k) != q[fa].end()){
res += 1ll * q[fa][k] * g.second;
}
}
}
for (auto & g : q[fb]){
q[fa][g.first] += g.second;
}
q[fb].clear();
sz[fa] += sz[fb];
}
}
int val[maxn * 3];
int main()
{
init();
int n , m; scanf("%d%d" , &n , &m);
for (int i = 1 ; i <= n + m; i++) sz[i] = 1 , f[i] = i;
for (int i = 1 ; i <= n ; i++){
int x ; scanf("%d" , &x);
val[i] = x;
q[i][x]++;
}
for (int i = 1 ; i <= m ; i++){
int op , a , b;scanf("%d%d%d" , &op , &a , &b);
if (op == 1){
f[a] = a;
sz[a] = 1;
q[a][b]++;
val[a] = b;
}
else if (op == 2){
mer(a , b);
}
else {
for (auto & g : fact[val[a]]){
res -= q[getf(a)][g];
}
q[getf(a)][val[a]]--;
q[getf(a)][b]++;
val[a] = b;
for (auto & g : fact[val[a]]){
res += q[getf(a)][g];
}
}
printf("%lld\n" , res);
}
return 0;
}