1. 程式人生 > >ACM-圖論-同餘最短路

ACM-圖論-同餘最短路

https://www.cnblogs.com/31415926535x/p/11692422.html

一種沒見過的處理模型,,記錄一下,,主要是用來處理一個多元一次方程的解的數量的問題,,資料量小時可以用看成揹包處理,,資料很大時可以轉換成最短路模型+一點數學來處理,,(體積模域下的最短路的問題,,求的一個最簡的表示形式有模數來得到所有解

墨墨的等式

題目

因為只是求滿足的解的數量,,所以可以將方程轉換成一個模方程組,,這樣的方程組的解顯然也是原來的解的子集,,同時可以利用模數來得到所有解,,

模數的選擇是最小的那個係數,,因為如果任意選擇,,會出現一些多考慮的情況

弄 mi 個點,表示從0到mi-1的所有數,,建邊的方法是 i->(i+a[j])%mi

邊權為 a[j] ,,表示從i這個點變成後面一個數的費用,,(因為兩邊都是取模的,,所以每一個數取幾次後的和的餘數就是那些經過的點,,也就是說一條路徑就是得到一個右邊為 i(mod mi) 的一個最小解,,這個最小的解就是費用和,,也就是一條最短路dis[i]

這樣我們對於每一個取模的右邊的B都計算一下區間裡的數量,,,(計算這玩意推錯了一次,,emmm

參考

#include <bits/stdc++.h>
#define aaa cout<<233<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
// mt19937 rnd(TM(0));
const int inf = 0x3f3f3f3f;//1061109567 > 1e9
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = 3.14159265358979;
const int maxn = 3e6 + 5;
const int maxm = 1e7 + 233;
const int mod = 1e9 + 7;

ll n, l, r, a[maxn];
struct edge
{
    int to, nxt; ll w;
}edge[maxn << 1];
int tot, head[maxn << 1];
void init()
{
    tot = 0;
    memset(head, -1, sizeof head);
}
void addedge(int u, int v, ll w)
{
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot++;
}
struct node
{
    int v; ll w;
    node(){}
    node(int _v, ll _w):v(_v), w(_w){}
    const bool operator<(const node &r)const{
        return w > r.w;
    }
}tmp;
ll dis[maxn];
bool vis[maxn];
void dijkstra(int s)
{
    memset(dis, inf, sizeof dis);
    memset(vis, false, sizeof vis);
    priority_queue<node> q;
    while(!q.empty())q.pop();
    q.push(node(s, 0));
    dis[s] = 0;
    while(!q.empty())
    {
        tmp = q.top(); q.pop();
        if(vis[tmp.v])continue;
        vis[tmp.v] = true;
        for(int i = head[tmp.v]; ~i; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if(dis[v] > dis[tmp.v] + edge[i].w)
            {
                dis[v] = dis[tmp.v] + edge[i].w;
                q.push(node(v, dis[v]));
            }
        }
    }
}
int main()
{
    // double pp = clock();
    // freopen("233.in", "r", stdin);
    // freopen("233.out", "w", stdout);
    ios_base::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    
    cin >> n >> l >> r;
    for(int i = 1; i <= n; ++i)cin >> a[i];
    sort(a + 1, a + 1 + n);
    int mi = a[1];
    init();
    for(int i = 0; i <= mi - 1; ++i)
        for(int j = 1; j <= n; ++j)
            addedge(i, (i + a[j]) % mi, a[j]);
    dijkstra(0);
    ll ans = 0;
    for(int i = 0; i <= mi - 1; ++i)
    {
        if(dis[i] <= r)
        {
            if(dis[i] == 0)dis[i] = mi;
            ans += (r - dis[i]) / mi + 1;
            if(l > dis[i])ans -= (l - dis[i] - 1) / mi + 1;
        }
    }
    cout << ans << endl;

    return 0;
}

P3403 跳樓機

題目

比上面那個簡單些,,就是注意細節,,從1開始,,有一個是1那麼值一定是h,,,

#include <bits/stdc++.h>
#define aaa cout<<233<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
// mt19937 rnd(TM(0));
const int inf = 0x3f3f3f3f;//1061109567 > 1e9
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = 3.14159265358979;
const int maxn = 3e6 + 5;
const int maxm = 1e7 + 233;
const int mod = 1e9 + 7;

ll n, l, r, a[maxn];
struct edge
{
    int to, nxt; ll w;
}edge[maxn << 1];
int tot, head[maxn << 1];
void init()
{
    tot = 0;
    memset(head, -1, sizeof head);
}
void addedge(int u, int v, ll w)
{
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot++;
}
struct node
{
    int v; ll w;
    node(){}
    node(int _v, ll _w):v(_v), w(_w){}
    const bool operator<(const node &r)const{
        return w > r.w;
    }
}tmp;
ll dis[maxn];
bool vis[maxn];
void dijkstra(int s)
{
    memset(dis, inf, sizeof dis);
    memset(vis, false, sizeof vis);
    priority_queue<node> q;
    while(!q.empty())q.pop();
    q.push(node(s, 0));
    dis[s] = 1;
    while(!q.empty())
    {
        tmp = q.top(); q.pop();
        if(vis[tmp.v])continue;
        vis[tmp.v] = true;
        for(int i = head[tmp.v]; ~i; i = edge[i].nxt)
        {
            int v = edge[i].to;
            if(dis[v] > dis[tmp.v] + edge[i].w)
            {
                dis[v] = dis[tmp.v] + edge[i].w;
                q.push(node(v, dis[v]));
            }
        }
    }
}
int main()
{
    // double pp = clock();
    // freopen("233.in", "r", stdin);
    // freopen("233.out", "w", stdout);
    ios_base::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    
    cin >> n;
    for(int i = 1; i <= 3; ++i)cin >> a[i];
    sort(a + 1, a + 1 + 3);
    if(a[1] == 1){
        cout << n << endl;
        return 0;
    }
    ll mi = a[1];
    init();
    for(int i = 0; i <= mi - 1; ++i)
        for(int j = 2; j <= 3; ++j) 
            addedge(i, (i + a[j]) % mi, a[j]);
    dijkstra(1);
    ll ans = 0;
    for(int i = 0; i <= mi - 1; ++i)
        if(dis[i] <= n)
            ans += (n - dis[i]) / mi + 1;
    cout << ans << endl;

    return 0;
}

遙遠的旅途

題目

這題的大致思路是將dp問題用最短路來優化,,

dp[i][j] 表示從起點走到i時的長度為j的一條路是否存在,,但是空間都會爆掉,,

考慮第二維,假設是通過經過若干個環來達到T,,也就是 len+kw==T ,,這裡的w即為環的長度的兩倍,,如果取模w就是 len%w==T%w ,,這樣子dp方程就變成了到達 i 點時路徑長度取模等於j的一條路徑的長度,,利用spfa來轉移,,只要最後 dp[n][T%w] <= T 就表示存在解,這樣子利用模數來壓縮了狀態,,找等同的就行了,,,參考 參考

#include <bits/stdc++.h>
#define aaa cout<<233<<endl;
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
// mt19937 rnd(TM(0));
const int inf = 0x3f3f3f3f;//1061109567 > 1e9
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const double pi = 3.14159265358979;
const int maxn = 1e2 + 5;
const int maxm = 1e7 + 233;
const int mod = 1e9 + 7;

ll n, m, T, a[maxn], ww;
struct edge
{
    int to, nxt; ll w;
}edge[maxn << 1];
int tot, head[maxn << 1];
void init()
{
    tot = 0;
    memset(head, -1, sizeof head);
}
void addedge(int u, int v, ll w)
{
    edge[tot].to = v;
    edge[tot].w = w;
    edge[tot].nxt = head[u];
    head[u] = tot++;
}
ll dp[maxn][20005];
bool vis[maxn][20005];
queue<pair<int, ll> > q;
void spfa()
{
    memset(dp, inf, sizeof dp);
    memset(vis, false, sizeof vis);
    while(!q.empty())q.pop();
    dp[1][0] = 0; vis[1][0] = true;
    q.push(make_pair(1, 0));
    while(!q.empty())
    {
        int u = q.front().first; ll w = q.front().second; q.pop(); 
        vis[u][w] = false;
        for(int i = head[u]; ~i; i = edge[i].nxt)
        {
            int v = edge[i].to; ll vw = edge[i].w;
            if(dp[v][(w + vw) % ww] > dp[u][w] + vw)
            {
                dp[v][(w + vw) % ww] = dp[u][w] + vw;
                if(!vis[v][(w + vw) % ww])
                {
                    vis[v][(w + vw) % ww] = true;
                    q.push(make_pair(v, (w + vw) % ww));
                }
            }
        }
    }
}
int main()
{
    // double pp = clock();
    // freopen("233.in", "r", stdin);
    // freopen("233.out", "w", stdout);
    ios_base::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    
    int t; cin >> t;
    while(t--)
    {
        cin >> n >> m >> T;
        int u, v, w;
        init();
        for(int i = 1; i <= m; ++i)
        {
            cin >> u >> v >> w;
            ++u, ++v;
            addedge(u, v, w);
            addedge(v, u, w);
        }
        bool flag = false;
        for(int i = head[n]; ~i; i = edge[i].nxt)
        {
            ww = edge[i].w << 1;
            spfa();
            if(dp[n][T % ww] <= T)
            {
                flag = true;
                break;
            }
        }
        if(flag)cout << "Possible" << endl;
        else cout << "Impossible" << endl;
    }

    return 0;
}

(e