标签:链接 ons pre 就是 pair priority out code cout
题目链接:https://ac.nowcoder.com/acm/contest/625/B
解法:这题其实就是求2^18个点内最近的两个点的距离。我们可以容易想到朴素解法:把每个点作为源点跑最短路取最小值。也很容易想到这个做法严重超时。
对于这种构图,这里有一个比较套路的方法:枚举2进制位数k,按二进制k位的0/1分成两组跑最短路。为什么是正确的,因为一旦两个数不等,那么这两个数必定在至少一个二进制位上不同,我们枚举了所有的二进制位,那么任意两个数都会被至少一次分到不同组中,亦即所有情况的最短路都考虑到了,那么当然是正确的。
这道题能得到一个启发:像这种对于一个很大很大的图,但是其实只有很少点是我们要计算的,我们可以考虑想出一种分组方式能使得任两个数都至少一次分到不同组,这就能减少时间而且不遗漏。
细节详见代码:
#include<bits/stdc++.h> #define int long long using namespace std; const long long P=19260817; const long long INF=(long long)1<<60; typedef long long LL; const int N=2e5+10; const int M=18; typedef pair<int,int> pii; int n,ans,a[N],cost[(1<<M)+10][M],d[(1<<M)+10],mark[(1<<M)+10]; bool vis[(1<<M)+10]; inline LL PowMod(LL a, LL b) { LL r=1; while(b) { if(b&1) r=r*a%P; a=a*a%P, b>>=1; } return r; } void prework() { for (int s = 0; s < (1<<18); ++s) { for(int i = 0; i < 18; ++i) { cost[s][i] = PowMod( max(s,s^(1<<i))%P, 1<<i ) % P + 1; } } } priority_queue<pii> q; void Dijkstra(int k) { while (!q.empty()) q.pop(); memset(d,0x3f,sizeof(d)); memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++) if (a[i]>>k & 1) { //把n个数字 分成2组 跑最短路 q.push(make_pair(0,a[i])); d[a[i]]=0; } while (!q.empty()) { pii x=q.top(); q.pop(); if (mark[x.second] && x.first!=0) ans=min(ans,-x.first); if (-x.first>=ans) return; //当前最短路都比答案大,后面的不用看 if (vis[x.second]) continue; vis[x.second]=1; for (int i=0;i<18;i++) { int y=x.second^(1<<i); if (d[y]>d[x.second]+cost[x.second][i]) { d[y]=d[x.second]+cost[x.second][i]; q.push(make_pair(-d[y],y)); } } } } signed main() { prework(); scanf("%lld",&n); for (int i=1;i<=n;i++) scanf("%lld",&a[i]),mark[a[i]]=1; sort(a+1,a+n+1); for (int i=2;i<=n;i++) if (a[i]==a[i-1]) { puts("0"); return 0; } ans=INF; for (int i=0;i<18;i++) Dijkstra(i); cout<<ans<<endl; return 0; }
2019华工校赛 B - 修仙时在做什么?有没有空?可以来炼丹吗?
标签:链接 ons pre 就是 pair priority out code cout
原文地址:https://www.cnblogs.com/clno1/p/10846960.html