ACM-圖論-同餘最短路
阿新 • • 發佈:2019-10-17
https://www.cnblogs.com/31415926535x/p/11692422.html
一種沒見過的處理模型,,記錄一下,,主要是用來處理一個多元一次方程的解的數量的問題,,資料量小時可以用看成揹包處理,,資料很大時可以轉換成最短路模型+一點數學來處理,,(體積模域下的最短路的問題,,求的一個最簡的表示形式有模數來得到所有解
墨墨的等式
題目
因為只是求滿足的解的數量,,所以可以將方程轉換成一個模方程組,,這樣的方程組的解顯然也是原來的解的子集,,同時可以利用模數來得到所有解,,
模數的選擇是最小的那個係數,,因為如果任意選擇,,會出現一些多考慮的情況
弄 mi 個點,表示從0到mi-1的所有數,,建邊的方法是 i->(i+a[j])%mi
這樣我們對於每一個取模的右邊的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