同餘最短路(洛谷P2371 墨墨的等式)

定義

  • 同餘最短路常用於解決這樣一類問題:

    nn個正整數a1,a2,a3,,ana_1, a_2,a_3 , \cdots, a_n,設:
    x1a1+x2a2+x3a3++xnan=k                (x1,x2,x3,,xnN)x_1a_1+x_2a_2+x_3a_3+\cdots+x_na_n=k\;\;\;\;\;\;\;\;(x_1,x_2,x_3,\cdots,x_n\in N)
    使用無限個a1,a2,a3,,ana_1,a_2,a_3,\cdots,a_n進行拼湊,然後對kk的可能值進行各種詢問(例如詢問kk[l,r][l,r]區間內的可能值個數,詢問kk最大的無法拼湊的值,詢問某個定值kk能否被拼湊出……)



算法思路

定義

取其中一個正整數作爲剩餘系,例如取a1a_1,設f(i)=min{kkmoda1=i}f(i)=\min\{k | k \bmod a_1 = i\} ,其中i=0,1,2,,a11i =0,1,2,\cdots,a_1-1f(i)f(i)爲即 可拼湊出使得kmoda1=ik \bmod a_1 = i最小kk

建圖

首先根據定義易知:f((i+aj)moda1)=min{f(i)+aj}f((i+a_j)\bmod a_1 )=\min\{f(i)+a_j\}f(0)=0f(0)=0,可以發現這和最短路的求法相同,

故可建圖G={V,E}G=\{V,E\},其中V={0,1,2,,a11}V=\{0,1,2,\cdots,a_1-1\}E={<u,v,w>(u+aj)moda1=vw=aj}E=\{<u,v,w>|(u+a_j) \bmod a_1 = v \land w=a_j\}

然後從點00開始,求一遍最短路,到點ii的最短路距離即爲f(i)f(i)。

結果

求得所有的f(i)f(i)後,

  • 所有可拼湊出的值的集合即爲{kk=f(i)+ta1  ,tN}\{k|k=f(i)+t\cdot a_1\;,t \in N \}
  • 最大的無法拼湊出的值爲max{kk=f(i)a1}\max\{k|k = f(i)-a_1\},若爲1-1則表示所有值均可拼湊出來;
  • 對於一個定值kk,若需要知道其具體的拼湊方案,則還需記錄最短路的具體路徑。
時間複雜度

nn個正整數有重複值,則最好要去重,且應當取最小aia_i作爲剩餘系,這樣圖更小,求最短路的時間複雜度也更優。

結點數V=mini=1n{ai}|V| = \min \limits_{i=1}^n \{a_i\},邊數E=n×V|E|=n\times |V|,利用堆優化Dijkstra算法求最短路時間複雜度爲O(E+VlogV)O(|E|+|V|\log|V|)



例題

洛谷P2371 墨墨的等式

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, int> PLI;
const int INF = 0x3f3f3f3f;
const int LINF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 5e5 + 10;

int n, a[maxn];
LL l, r, dist[maxn];
struct edge
{
    int v;
    int w;
};
vector<edge> g[maxn];
bool vis[maxn];

void Dijkstra(int s)
{
    memset(dist, 0x3f, sizeof(dist));
    priority_queue<PLI, vector<PLI>, greater<PLI> > q;
    dist[s] = 0;
    q.push(make_pair(dist[s], s));
    while(!q.empty())
    {
        int u = q.top().second;
        q.pop();
        if(vis[u])
            continue;
        else
            vis[u] = true;
        for(auto e : g[u])
        {
            int v = e.v, w = e.w;
            if(!vis[v] && dist[u] + w < dist[v])
            {
                dist[v] = dist[u] + w;
                q.push(make_pair(dist[v], v));
            }
        }
    }
}
int main()
{
    scanf("%d %lld %lld", &n, &l, &r);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    sort(a + 1, a + n + 1);
    n = unique(a + 1, a + n + 1) - (a + 1);
    for(int i = 0; i < a[1]; i++)
        for(int j = 2; j <= n; j++)
            g[i].push_back(edge{(i + a[j]) % a[1], a[j]});
    Dijkstra(0);
    LL ans = 0;
    for(int i = 0; i < a[1]; i++)
    {
        if(r >= dist[i])
            ans += (r- dist[i]) / a[1] + 1;
        if(l - 1 >= dist[i])
            ans -= ((l - 1) - dist[i]) / a[1] + 1;
    }
    printf("%lld\n", ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章