P16495 踏青南陌上,寄远展壮游
满足条件的 \(u, v\) 可以连边。一开始读错题了,没看到“可以重复访问同一个景点”。加上这个条件那就是同一个连通块内的点都可以访问了。
所以就是并查集查有多少个连通块。
直接连边是 \(O(q ^ 2)\) 的,我们是接受不了的。发现当 \(a_u \ or\ a_v=2n−1\) 时,\(a_u\) 与 \(a_v\) 二进制上某一位的数字均为 \(0\)。
于是考虑为这 \(n\) 位都建一个虚点,若景点 \(u\) 满足 \(a_u\ and\ 2^k=0\),则景点$ u$ 和景点 \(2k\) 可连一条边。每加入一个景点只需考虑这 n 个点即可。
时间复杂度 \(O(qn)\)。答案统计就是查 \(siz > 1\) 的连通块。注意特判孤立点 \(2^n - 1\)。
#include<bits/stdc++.h>
#define F(i, l, r) for(int i(l); i <= (r); ++ i)
#define G(i, r, l) for(int i(r); i >= (l); -- i)
using namespace std;
using ll = long long;
const int N = 6e5;
int n, q;
int fa[N], siz[N];
int get(int x){
if(fa[x] != x) return fa[x] = get(fa[x]);
return x;
}
void merge(int u, int v){
u = get(u);
v = get(v);
if(u != v){
fa[v] = u;
siz[u] += siz[v];
}
}
void Main(){
cin >> n >> q;
F(i, 1, q + n){
fa[i] = i;
siz[i] = 1;
}
ll mx = (1ll << n) - 1;
int cnt = 0;
F(i, 1, q){
ll x;
int ans = 0;
cin >> x;
if(mx == x){
++ cnt;
}
else{
F(j, 0, n - 1){
if(((x >> j) & 1ll) == 0){
merge(j + q + 1, i);
}
}
}
F(j, 1, n){
// printf("fa[%d] = %d\tsiz[%d] = %d\n", j + q, get(j + q), get(j + q), siz[get(j + q)]);
if(get(j + q) == j + q && siz[j + q] > 1){
++ ans;
}
}
cout << ans + cnt << '\n';
}
}
signed main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T = 1;
while(T --) Main();
return fflush(0), 0;
}

浙公网安备 33010602011771号