1. 程式人生 > 資訊 >用友汽車科創板上市通過

用友汽車科創板上市通過

計組課划水研究了一下(

參考:OI Wiki

介紹

常見題型

  • 給定 n 個整數,求這 n 個整數能拼湊出多少的其他整數( n 個整數可以重複取)
  • 給定 n 個整數,求這 n 個整數不能拼湊出的最小(最大)的整數
  • 至少要拼幾次才能拼出模 k 餘 p 的數

簡介

同餘最短路利用同餘來構造一些狀態,可以達到優化空間複雜度的目的

類比差分約束方法,利用同餘構造的這些狀態可以看作單源最短路中的點。同餘最短路的狀態轉移通常是這樣的 f(i+y) = f(i) + y,類似單源最短路中 f(v) = f(u) + edge(u,v)

例題

P3403 跳樓機

傳送門:https://www.luogu.com.cn/problem/P3403

題目大意:給定x, y, z, h,對於 k ∈ [1,h],有多少個 k 能滿足 ax + by + cz = k

令 dis(i) 表示僅通過 操作 2操作 3 能到達的 mod x = i 的最小樓層

可以得到如下兩個狀態:

  • dis(i + y) = dis(i) + y
  • dis(i + z) = dis(i) + z

那麼實際上相當於執行了最短路中的建邊操作:

  • add(i, (i + y) % x, y)
  • add(i, (i + z) % x, z)

跑一次最短路就可以求出相應的 dis(i),答案即為 ans += (h − f[i]) / x + 1(+1 是因為當前所在樓層也算)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
const int INF = 0x3f3f3f3f;
ll h, x, y, z;
ll head[maxn << 1], tot;
ll dis[maxn], vis[maxn];
queue<int> q;
struct edge {
  ll to, next, w;
} e[maxn << 1];
void add(ll u, ll v, ll w) {
  e[++tot] = edge{v, head[u], w};
  head[u] = tot;
}
void spfa() {
  dis[1] = 1;
  vis[1] = 1;
  q.push(1);
  while (!q.empty()) {
    int u = q.front();
    q.pop();
    vis[u] = 0;
    for (int i = head[u]; i; i = e[i].next) {
      int v = e[i].to, w = e[i].w;
      if (dis[v] > dis[u] + w) {
        dis[v] = dis[u] + w;
        if (!vis[v]) {
          q.push(v);
          vis[v] = 1;
        }
      }
    }
  }
}
int main() {
  memset(dis, INF, sizeof(dis));
  scanf("%lld", &h);
  scanf("%lld %lld %lld", &x, &y, &z);
  if (x == 1 || y == 1 || z == 1) {
    printf("%d\n", h);
    return 0;
  }
  for (int i = 0; i < x; i++) {
    add(i, (i + z) % x, z);
    add(i, (i + y) % x, y);
  }
  spfa();
  ll ans = 0;
  for (int i = 0; i < x; i++) 
    if (h >= dis[i]) ans += (h - dis[i]) / x + 1;
  printf("%lld\n", ans);
  return 0;
}