题目可以转换成
对取反,得到
,如果
是
在二进制下的子集,则可以建边
因为这是基于数的建边(可能有多个值相同),复杂度简单考虑:
如果每个数只出现一次,那么至多出现的二进制()的子集数(
)
并且边的长度都是,所以可以这么
出去。
特殊情况,基于值所以,对于,同样值的别的点就不是
了。
对于这一类点:
判断是不是,因为
只要一步到达。
如果不是,若能发出一条边,那么距离是,因为
,上面建边的过程永远都是可以相互走的。
否则无法到达。
一直卡这个特判,看了别人的才反应过来
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int K = 19; const int maxn = 1e6 + 100; const int inf = 0x3f3f3f3f; const int all = (1<<18)-1; int dp[maxn],ans[maxn]; int n,s,a[maxn],vis[maxn],cnt[maxn]; queue<int>q; int main(){ cin>>n>>s; for(int i=1;i<=n;i++)scanf("%d",a+i),cnt[a[i]]++; memset(dp,0x3f,sizeof(dp)); dp[a[s]]=0; q.push(a[s]); while(!q.empty()){ int u=q.front();q.pop(); if(vis[u])continue; vis[u]=1; int tmp=all^u; for(int v=tmp;;v=((v-1)&tmp)){ if(cnt[v]){ dp[v]=min(dp[v],dp[u]+1); q.push(v); } if(v==0)break; // cout<<((v-1)&u)<<endl; } } bool ok=false; int tmp=a[s]^all; for(int v=tmp;;v=((v-1)&tmp)){ if(cnt[v])ok=true; if(v==0)break; } for(int i=1;i<=n;i++){ if(dp[a[i]]==inf)ans[i]=-1; else if(a[i]==a[s]){ if(a[i]==0)ans[i]=1; else if(ok)ans[i]=2; else ans[i]=-1; } else ans[i]=dp[a[i]]; } ans[s]=0; for(int i=1;i<=n;i++)printf("%d%c",ans[i],i==n?'\n':' '); }
SOS看懂了,但是用在这里真心写不来