P16495 踏青南陌上,寄远展壮游

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;
}
posted @ 2026-10-12 19:42  superl61  阅读(7)  评论(0)    收藏  举报