The 17-th BIT Campus Programming Contest C
\(\Huge{The 17-th BIT Campus Programming Contest}\)
\(\huge{C.小L的旅行}\)
题目链接:The 17-th BIT Campus Programming Contest
题意
给出一个\(n\)个点的有向图,有\(m\)条边且每条边的边权为\(1\)。每个点对应有一个魔法值,对于任意两点,若存在:\(a_i \& a_j = a_j\),那么就存在一条从\(i\)到\(j\)的边,边权为\(1\)。
求从\(1\)到其他点的最短距离。
思路
题目要求求最短路,因此考虑使用\(Dijkstra\)。由于边权都为\(1\),我们考虑优化\(Dijkstra\),可以使用双端队列优化掉优先队列。
然后考虑加边(将根据魔法值建的边加入到图中)。
-
满足\(a_i \& a_j = a_j\),则\(a_j\)是由\(a_i\)的二进制下去掉若干\(1\)得到。
-
求一个数的所有符合\(a_i \& a_j = a_j\)条件的值的时间复杂度太大
-
考虑只将\(a_i\)与其二进制下某一位1变为0得到的数之间建边
-
这样\(a_i\)将会到达所有能够建边的数,虽然不是强连通,但是边权都为\(0\)。
标程
#define int long long
const int INF = 0x7fffffff;
const int N = 2e6 + 10;
vector<int> d(N), dis(N, INF);
vector<PII> g[N];
vector<bool> vis(N);
void Solved() {
int n, m; cin >> n >> m;
for(int i = 1; i <= n; i ++ ) cin >> d[i];
for(int i = 1; i <= m; i ++ ) {
int x, y; cin >> x >> y;
g[x].push_back({y, 1});
}
for(int i = 0; i < (1 << 20); i ++ ) //预处理出根据魔法值建的边(建在新图中)
for(int j = 0; j < 20; j ++ )
if((i >> j) & 1)
g[n + i + 1].push_back({n + 1 + (i ^ (1 << j)), 0});
for(int i = 1; i <= n; i ++ ) { //合并两图
g[i].push_back({n + 1 + d[i], 1}); //代表使用魔法,边权为1
g[n + 1 + d[i]].push_back({i, 0}); //能进入新图则表示边权已+1,此时不需要加边权
}
dis[1] = 0;
deque<int> dq; //双端队列优化
dq.push_back(1);
while(dq.size()) {
int u = dq.front(); dq.pop_front(); //每次取队列头元素
if(vis[u]) continue;
vis[u] = true;
for(auto [i, j] : g[u]) {
if(dis[i] <= dis[u] + j) continue;
dis[i] = dis[u] + j;
if(j) dq.push_back(i); //优化的具体方法,及边权为1则放在末尾
else dq.push_front(i); //边权为0则放在开头
}
}
for(int i = 1; i <= n; i ++ ) {
if(dis[i] != INF) {
cout << dis[i] << endl;
} else {
cout << "-1\n";
}
}
}

浙公网安备 33010602011771号