把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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;
}
posted @ 2020-04-22 23:59  Starlight_Glimmer  阅读(5)  评论(0编辑  收藏  举报  来源
浏览器标题切换
浏览器标题切换end