遊記:第37屆校賽

Catherine_leah發表於2024-11-08

T7 悅跑圈!啟動!

改完這個題我就睡覺

求在s1或s2閉合的最小環,所以我列舉了邊,假設一條邊的兩頭分別是u和v,我以為環就是dij跑出來的d[u]+d[v],雖然不會立刻原路返回起點,但是中間可能有重複路徑,而環是不能有這種路徑的,所以從一開始就錯了。(不長記性,失戀三部曲忘了)

因為要找從s出發要回到s的最小環,路徑一定是s->和s相連的某個點->和s相連的另一個點->s,如果記錄和s相連的另一個點到s的路徑為L1(單獨的一條邊),再找到在不經過L1的情況下(也就是不從入點出發,也就是從出點出發得到的最短路)從s出發到達【和s相連的另一個點】的最短路徑,答案就是求和(的最小值)。

把【和s相連的某個點】記為出點集合,把【和s相連的另一個點】記為入點集合,我們只需要先分組再透過一些處理使得從s到其他所有點的最短路徑一定是s->出點中的一個->……,最小的【d[入點之一]+這個入點和s之間的邊長】即為所求。

分組方式是“二進位制分組”,這並不是狀態壓縮裡的那種列舉,因為我們不需要所有出點入點分組的情況,我們只需要保證任意兩點都有不同組的機會。

遊記:第37屆校賽

至於“透過處理使得所有最短路從s出發後第一個經過的一定是一個出點”,可以對dij進行一下魔改,也就是提前把出點和它們的邊權放進優先佇列,我們擴充的起點不再是d[s]=0,而是最近的一個出點,跳過s,那些入點根本不會在第一輪進佇列,也就不會被更新。

評測系統沒開,程式碼正確性未知,先放在這裡好了。

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e6 + 2;

int n, m, T, s1, s2;
ll d[maxn], ans = 1e9 + 15;
bool vis[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node 
{
    int nxt, to, w;
}a[maxn<<1];
int head[maxn], len;

void add(int x, int y, int w)
{
    a[++len].to = y; a[len].nxt = head[x]; a[len].w = w;
    head[x] = len;
}
int vec[maxn], cnt, ps, pt, s[maxn], t[maxn];
ll vd[maxn], sd[maxn], td[maxn];

void dij(int op)
{
    memset(vis, 0, sizeof(vis));
    memset(d, 0x3f, sizeof(d));
    priority_queue<pair<int, int>, vector<pair<int,int> >, greater<pair<int, int> > > q;
    for(int i=1; i<=ps; i++)
    {
        q.push(make_pair(sd[i], s[i])); d[s[i]] = sd[i];
    }
    vis[op] = 1;
    while(!q.empty())
    {
        int u = q.top().second; q.pop();
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i=head[u]; i; i=a[i].nxt)
        {
            int v = a[i].to, ds = a[i].w;
            if(vis[v]) continue;
            if(d[v] > d[u] + ds)
            {
                d[v] = d[u] + ds;
                q.push(make_pair(d[v], v));
            }
        }
    }
    for(int i=1; i<=pt; i++)
    {
        ans = min(ans, d[t[i]]+td[i]);
    }
}

int main()
{
    n = read(); m = read(); T = read();
    s1 = read(); s2 = read();
    for(int i=1; i<=m; i++)
    {
        int u = read(), v = read(), w = read();
        add(u, v, w); add(v, u, w);
    }
    //求以s1為起點的最小環
    for(int i=head[s1]; i; i=a[i].nxt)
    {
        vec[++cnt] = a[i].to; vd[cnt] = a[i].w;
    }
    for(int i=1,j=1; j<=32; i<<=1,j++)
    {
        ps = pt = 0;
        for(int k=1; k<=cnt; k++)
        {
            if(k & i) s[++ps] = vec[k], sd[ps] = vd[k];
            else t[++pt] = vec[k], td[pt] = vd[k];
        }
        if(!ps || !pt) continue;//全都進同一組了的情況有嗎?
        dij(s1);
    }
    //求以s2為起點的最小環
    for(int i=head[s2]; i; i=a[i].nxt)
    {
        vec[++cnt] = a[i].to; vd[cnt] = a[i].w;
    }
    for(int i=1,j=1; j<=32; i<<=1,j++)
    {
        ps = pt = 0;
        for(int k=1; k<=cnt; k++)
        {
            if(k & i) s[++ps] = vec[k], sd[ps] = vd[k];
            else t[++pt] = vec[k], td[pt] = vd[k];
        }
        if(!ps || !pt) continue;
        dij(s2);
    }
    ans = T / ans;
    printf("%lld", ans);

    return 0;
}

相關文章