CF1206(Div.2)D Shortest Cycle
当时打cf的时候前面3题都做得飞快(虽然T3的结论没有严谨证明,但是前面3题都直接过PP,最后AC的
结果到D题就干不动了
如果只是个裸的最短环的话,还比较好办,但是这题数据范围太大,挨个枚举点相与来判断两点之间有没有边的话,建图都会超时。下不了手[无奈.jpg]
最后还是大佬一语点醒了梦中人,好吧,这道题需要一个结论(怎么又是结论,我最不擅长结论了嘤嘤嘤):
如果,某一位上为1的数不小于3个,那么答案一定是3(这其中的任意3个点都互相连通,可以组成一个环);考虑到数这么多,而 1 0 18 10^{18} 1018的二进制最多只有 60 60 60位,如果不是第一种情况的话,那么每一位为1的数最多有2个,总共的有效参与计算的点最多只有120个,这样就可以有效地缩小数据范围,然后直接找环啦。
找环的话,由于是边权为1,所以可以用 b f s bfs bfs。有边相连的两个点,如果从其中1个点bfs最短路(不经过直接相连的边)可以到达另一个点,最短路长度+1(加上直接相连的那条边)就是最短环,如果不能到达,就没有环。对于每一对有边相连的两个点都进行一次这样的操作,最小值就是答案。
我还有一种想法,就是像tarjan那样,经过一条返祖边就是一个环,把每个环都算出来,求最小就可以了。(大佬说可以,不过这个还没有写。。。毕竟 b f s bfs bfs更好打。。。)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define N 100005
#define M 150 /*60位二进制数 每位为1最多2个 最多20个数*/
#define ll long long
#define INF 0x3f3f3f3f
int n,cnt;
ll a[N],b[M];
int d[M];
queue<int> Q;
int dis(int x,int y)
{//边权为1 相当于bfs
memset(d,255,sizeof(d));//清-1
//printf("%d\n",dis[0]);
d[x]=0;
Q.push(x);
while(!Q.empty())
{
int i=Q.front();Q.pop();
for(int j=1;j<=cnt;j++)
if((b[i]&b[j])&&d[j]==-1&&(i!=x||j!=y))
{
Q.push(j);
d[j]=d[i]+1;
}
}
if(d[y]==-1) return INF;
return d[y];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i]);
for(int i=1;i<=n;i++)
for(int j=60;j>=0;j--)
if((a[i]>>j)&1)//第j位有1
b[j]++;
for(int j=0;j<=60;j++)
if(b[j]>=3)
{
puts("3");
return 0;
}
cnt=0;
for(int i=1;i<=n;i++)
if(a[i])//如果为0,则不可能与别人与起来为1
b[++cnt]=a[i];
int ans=INF;
for(int i=1;i<=cnt;i++)
for(int j=i+1;j<=cnt;j++)
if(b[i]&b[j])
ans=min(ans,dis(i,j)+1);
if(ans==INF) puts("-1");
else printf("%d\n",ans);
return 0;
}