w
z
l
wzl
wzl大佬在群里提了一下的题,感觉不错就放上来了。
题目给你
n
≤
1
e
5
n\leq1e5
n≤1e5个正整数,然后两点之前连通的条件是
a
[
i
]
&
a
[
j
]
≠
0
a[i]\&a[j]≠0
a[i]&a[j]̸=0,然后要求这张图的最小环是多大,如果不存在最小环,就输出
−
1
-1
−1。
首先,拆位考虑每一位,如果某一位超过三个数,那么最小环是
3
3
3。否则每一位都不超过两个数,把一位的两个数直接连边。然后枚举所有的边,把这条边去掉再去跑
B
F
S
BFS
BFS,如果连通那么就成环,并且环的大小是最短路
+
1
+1
+1,更新答案。
时间复杂度
O
(
n
l
o
g
n
+
l
o
g
2
n
)
O(nlogn+log^2n)
O(nlogn+log2n)
我写的比较劣,不必要写spfa的。
另外有一个东西值得一提,就是vector这个容器在执行erase的时候,这个迭代器就没有了,直接自增迭代器是有问题的。正确的写法应该是满足某个条件的时候把迭代器设为erase的返回值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=1e5+7;
ll a[N];
int n;
int cnt[63];
int dis[N];
bool inq[N];
vector<int> G[N];
struct edge{ int u,v; } e[N];
int tot=0;
int spfa(int u,int v) {
for(int i=1;i<=n;i++) {
dis[i]=1e9;
inq[i]=0;
}
queue<int> q;
q.push(u);
inq[u]=1;
dis[u]=0;
while(!q.empty()) {
int u=q.front();
q.pop();
for(auto &v:G[u]) {
if(dis[v]>dis[u]+1) {
dis[v]=dis[u]+1;
if(!inq[v]) {
inq[v]=1;
q.push(v);
}
}
}
}
return dis[v];
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%lld",&a[i]);
for(int j=0;j<=62;j++) {
if(a[i]&(1LL<<j)) cnt[j]++;
}
}
int ok=0;
for(int i=0;i<=62;i++) {
if(cnt[i]>=3) {
ok=1;
break;
}
}
if(ok) puts("3");
else {
int ans=1e9;
for(int i=0;i<=62;i++) {
if(cnt[i]!=2) continue;
int u=0,v=0;
for(int j=1;j<=n;j++) {
if(a[j]&(1LL<<i)) {
if(u==0) u=j;
else v=j;
}
}
G[u].push_back(v);
G[v].push_back(u);
e[++tot]={u,v};
}
for(int i=1;i<=tot;i++) {
int u=e[i].u;
int v=e[i].v;
int cnt=0;
for(auto it=G[u].begin();it!=G[u].end();) {
if(*it==v) it=G[u].erase(it),cnt++;
else it++;
}
for(auto it=G[v].begin();it!=G[v].end();) {
if(*it==u) it=G[v].erase(it);
else it++;
}
int cur=spfa(u,v);
if(cur<1e9&&cur>=2) ans=min(ans,cur+1);
for(int i=1;i<=cnt;i++) {
G[u].push_back(v);
G[v].push_back(u);
}
}
if(ans==1e9) puts("-1");
else printf("%d\n",ans);
}
return 0;
}