0%

2025“钉耙编程”中国大学生算法设计暑期联赛(1)——树上LCM

思路讲解

三个集合的容斥原理

$\left | A \cup B \cup C \right | = \left | A \right | + \left | B \right | + \left | C \right | - \left | A \cap B \right | - \left | A \cap C \right | - \left | B \cap C \right | + \left | A \cap B \cap C \right | $

image

那么不难发现规律,就是奇加偶减。这就是这段代码的由来。

1
2
3
ll cn=bi.count();
if(cn%2==1) del+=(sz+1)*sz/2;
else del-=(sz+1)*(sz)/2;

那么直接计算合法路径数量比较难,但是我们可以枚举不合法子集。如果s是101(二进制),那么就是第一个素因子要不符合要求(LCM只关注这些数当中的最大的,你可以理解为对素因子取了一个并集,那么gcd就是取了一个交集)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// s如果这一位是1,就代表这一位的质因数必须不满足要求
FOR(s,0,(1<<siz)-1){
vb vis(N+5,0);
bitset<11> bi(s);
// 检查该数字(该点)是否满足要求
auto check=[&](ll u)->bool{
FOR(i,0,siz-1){
if(!bi[i]) continue;
// 该数不满足要求,因为我们要求这一位质因数数量缺失,或者太多
if(fach[u][i]==cnt[i]){
return false;
}
}
return true;
};
FOR(u,1,N){
if(vis[u]) continue;
if(!valid[u]) continue;
vis[u]=true;
if(!check(u)) continue;
queue<ll> q;q.push(u);
ll sz=0; // 维护联通块内的点的数量
while(!q.empty()){
ll u=q.front();q.pop();
vis[u]=true;
++sz;
for(auto v:g[u]){
if(vis[v]) continue;
if(!valid[v]) continue;
if(!check(v)) continue;
q.push(v);
}
}
// sz*(sz-1)/2+sz
ll cn=bi.count();
if(cn%2==1) del+=(sz+1)*sz/2;
else del-=(sz+1)*(sz)/2;
// ans+=(sz+1)*sz/2;
}
#ifdef LOCAL
debug(del);
#endif
}
ll ans=-del;
cout<<ans<<"\n";
}

AC代码

https://acm.hdu.edu.cn/contest/view-code?cid=1172&rid=22776

心路历程(WA,TLE,MLE……)