【牛客】牛客练习赛67-E-牛妹游历城市——位运算优化

题面链接

大致题意

给出 n n n 个节点的权值,如果两个点的权值 a n d and and 的结果不为 0 0 0 则认为这两个点之间有边相连,且边权为 l o w b i t ( a & b ) lowbit(a \& b) lowbit(a&b) 求问从 1 1 1 走到 n n n 点,最短路径为多少

分析

首先不能暴力

因为点数有 1 0 5 10^5 105 个,所有可能的边的数量为 1 0 5 ∗ ( 1 0 5 − 1 ) / 2 ≈ 1 0 10 10^5 * (10^5-1) / 2 \approx 10^{10} 105(1051)/21010

考虑位运算优化

我们准备出 32 个组,对于每一个值,如果这个值 a a a 在位置 i i i 上有值,即 a & ( 1 < < i ) ≠ 0 a \& (1 << i ) \neq 0 a&(1<<i)=0 则这个值划分至这个组中,规定每个值可以属于多个组

对于每个组,我们再定义一个变量表示到达组 i i i 中的值需要的边权价值 t t t t ≥ ( 1 < < i ) t \geq (1 << i) t(1<<i)

对于起点 1 1 1 ,我们考虑它的每一个位,如果位 i i i 上有值,则认为到达这个位置的组中的其他所有值需要花费 ( 1 < < i ) (1 << i) (1<<i)。记录下每个组的花费,然后对被遍历到的每一组中的每一个值进行类似起点的操作,类似 B e l l m a n − F o r d Bellman-Ford BellmanFord 算法进行松弛,直到整个图没有发生更新花费

最后考虑最终点的每一位,找出花费最少的位并输出即可

注意特判起点和终点相同的时候

AC code

#include 

using namespace std;

typedef long long ll;

// NOLINTNEXTLINE
ll lowBit(ll x) { return x & (-x); }

void solve() {
    ll _;
    cin >> _;
    for (ll ts = 0; ts < _; ++ts) {
        ll n;
        cin >> n;
        vector<ll> data[40];
        vector<ll> cost(40, LONG_LONG_MAX);
        vector<ll> a(n);
        for (ll i = 0; i < n; ++i) {
            cin >> a[i];
            for (ll j = 0; j < 40; ++j)
                // NOLINTNEXTLINE
                if (a[i] & (1ll << j))
                    data[j].push_back(i);
        }
        if (n == 1) {
            cout << 0 << endl;
            continue;
        }
        queue<ll> q;
        for (ll i = 0; i < 40; ++i) {
            // NOLINTNEXTLINE
            if (a[0] & (1ll << i)) {
                q.push(i);
                // NOLINTNEXTLINE
                cost[i] = 1ll << i;
            }
        }

        while (!q.empty()) {
            ll cur = q.front();
            q.pop();
            for (auto item : data[cur]) {
                for (ll i = 0; i < 40; ++i) {
                    // NOLINTNEXTLINE
                    if ((a[item] & (1ll << i)) && cost[i] > (1ll << i) + cost[cur]) {
                        q.push(i);
                        // NOLINTNEXTLINE
                        cost[i] = (1ll << i) + cost[cur];
                    }
                }
            }
        }

        ll ans = LONG_LONG_MAX;
        for (ll i = 0; i < 40; ++i) {
            // NOLINTNEXTLINE
            if (a[n - 1] & (1ll << i)) {
                ans = min(ans, cost[i]);
            }
        }

        if (ans == LONG_LONG_MAX)
            cout << "Impossible" << endl;
        else
            cout << ans << endl;
    }
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
#ifdef ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    signed localTestCount = 1, localReadPos = cin.tellg();
    char localTryReadChar;
    do {
        if (localTestCount > 20)
            throw runtime_error("Check the stdin!!!");
        auto startClockForDebug = clock();
        solve();
        auto endClockForDebug = clock();
        cout << "Test " << localTestCount << " successful" << endl;
        cerr << "Test " << localTestCount++ << " Run Time: "
             << double(endClockForDebug - startClockForDebug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (localReadPos != cin.tellg() && cin >> localTryReadChar && localTryReadChar != '$' &&
             cin.putback(localTryReadChar));
#else
    solve();
#endif
    return 0;
}

你可能感兴趣的:(#,图论,ACM)