简介 
同余最短路是用最短路的方式求解「用若干个整数凑出其它整数」这一问题,可以看做类似于背包问题的一种 DP,但是需要用到最短路求解。
它的思想比较神奇,设我们有的数列是 a a a a 1 a_1 a 1  f ( i ) f(i) f ( i ) x   m o d   a 1 = i x \bmod a_1= i x mod a 1  = i x x x f f f k k k f ( k   m o d   a 1 ) ≤ k f(k\bmod a_1) \le k f ( k mod a 1  ) ≤ k f ( k   m o d   a 1 ) f(k\bmod a_1) f ( k mod a 1  ) a 1 a_1 a 1  k k k 
这个状态转移应该这么进行:
f ( i ) = min  i = 1 n { f ( ( j + a i )   m o d   a 1 ) + a i } f(i)=\min_{i=1}^n \{f((j+a_i)\bmod a_1) + a_i\}
 f ( i ) = i = 1 min n  { f (( j + a i  ) mod a 1  ) + a i  } 
满足一个最短路的形式,所以可以用最短路求解。
[POI2003] Sums 
我们给定一个整数集合 A A A A ′ A' A ′ A ′ A' A ′ x x x A A A 
比如,当 A = { 2 , 5 , 7 } A = \{2,5,7\} A = { 2 , 5 , 7 } A ′ A' A ′ 0 0 0 0 0 0 2 2 2 4 4 4 2 + 2 2 + 2 2 + 2 12 12 12 5 + 7 5 + 7 5 + 7 7 + 5 7 + 5 7 + 5 2 + 2 + 2 + 2 + 2 + 2 2 + 2 + 2 + 2 + 2 + 2 2 + 2 + 2 + 2 + 2 + 2 1 1 1 3 3 3 A ′ A' A ′ 
输入格式 
第一行有一个整数 n n n A A A a i a_i a i  A = { a 1 , a 2 , . . . , a n } A = \{a_1,a_2,...,a_n\} A = { a 1  , a 2  , ... , a n  } 
接下来一个整数 k k k b 1 , b 2 , . . . , b k b_1,b_2,...,b_k b 1  , b 2  , ... , b k  
输出格式 
输出 k k k b i b_i b i  A ′ A' A ′ i i i TAK,否则打印 NIE。
对于所有数据,1 ≤ n ≤ 5 × 1 0 3 1 \le n \le 5 \times 10^3 1 ≤ n ≤ 5 × 1 0 3 1 ≤ k ≤ 1 0 4 1 \le k \le 10^4 1 ≤ k ≤ 1 0 4 1 ≤ a 1 < a 2 < . . . < a n ≤ 5 × 1 0 4 1 \le a_1 < a_2 < ... < a_n \le 5 \times 10^4 1 ≤ a 1  < a 2  < ... < a n  ≤ 5 × 1 0 4 0 ≤ b i ≤ 1 0 9 0 \le b_i \le 10^9 0 ≤ b i  ≤ 1 0 9 
题目链接:P8060 。
 
和前文说的一样,跑这个最短路的时候不用真的把图建出来,直接枚举 a i a_i a i  
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include  <bits/stdc++.h>  using  namespace  std;const  int  N = 50010 ;int  n, k, a[N], dist[N];typedef  pair<int , int > PII;void  dijkstra ()      bitset<N> st;     memset (dist, 0x3f , sizeof  dist);     priority_queue<PII, vector<PII>, greater<PII>> heap;     dist[0 ] = 0 ;     heap.push ({0 , 0 });     while  (heap.size ()) {         int  t = heap.top ().second; heap.pop ();         if  (st[t]) continue ;         st[t] = true ;         for  (int  i = 1 ; i <= n; i++) {             int  to = (t + a[i]) % a[1 ], w = a[i];             if  (dist[to] > dist[t] + w) {                 dist[to] = dist[t] + w;                 heap.push ({dist[to], to});             }         }     } } int  main ()      scanf ("%d" , &n);     for  (int  i = 1 ; i <= n; i++) scanf ("%d" , &a[i]);     dijkstra ();          scanf ("%d" , &k);     for  (int  i = 1 , b; i <= k; i++) {         scanf ("%d" , &b);         if  (dist[b % a[1 ]] > b) puts ("NIE" );         else  puts ("TAK" );     }     return  0 ; } 
[国家集训队] 墨墨的等式 
墨墨突然对等式很感兴趣,他正在研究 ∑ i = 1 n a i x i = b \sum_{i=1}^n a_ix_i=b ∑ i = 1 n  a i  x i  = b n , a 1 … n , l , r n, a_{1\dots n}, l, r n , a 1 … n  , l , r b ∈ [ l , r ] b\in[l,r] b ∈ [ l , r ] 
输入格式 
第一行三个整数 n , l , r n,l,r n , l , r 
第二行 n n n a 1 … n a_{1\dots n} a 1 … n  
输出格式 
一行一个整数,表示有多少 b ∈ [ l , r ] b\in[l,r] b ∈ [ l , r ] 
对于 100 % 100\% 100% n ≤ 12 n \le 12 n ≤ 12 0 ≤ a i ≤ 5 × 1 0 5 0 \le a_i \le 5\times 10^5 0 ≤ a i  ≤ 5 × 1 0 5 1 ≤ l ≤ r ≤ 1 0 12 1 \le l \le r \le 10^{12} 1 ≤ l ≤ r ≤ 1 0 12 
题目链接:P2371 。
 
首先把数列里的 0 0 0 0 ∼ l − 1 , 0 ∼ r 0\sim l-1,0\sim r 0 ∼ l − 1 , 0 ∼ r 0 ∼ x 0\sim x 0 ∼ x 
枚举 i i i a 1 a_1 a 1  0 ∼ x 0\sim x 0 ∼ x mod  a 1 = i \text{mod }a_1 = i mod  a 1  = i d i s t ( i ) ∼ x dist(i)\sim x d i s t ( i ) ∼ x mod  a 1 = i \text{mod }a_1= i mod  a 1  = i 
首先讨论一个更一般化的问题,在 [ L , R ] [L,R] [ L , R ] mod  p = r \text{mod }p = r mod  p = r L ≤ k p + r ≤ R L\le kp+r\le R L ≤ k p + r ≤ R k k k k k k k k k 
⌊ R − r p ⌋ − ⌈ L − R p ⌉ + 1 \lfloor\frac{R-r}{p}\rfloor - \lceil\frac{L-R}{p}\rceil+1
 ⌊ p R − r  ⌋ − ⌈ p L − R  ⌉ + 1 
回到本题,也可以套公式:
⌊ x − i a 1 ⌋ − ⌈ d i s t ( i ) − i a 1 ⌉ + 1 \lfloor\frac{x-i}{a_1}\rfloor-\lceil\frac{dist(i)-i}{a_1}\rceil+1
 ⌊ a 1  x − i  ⌋ − ⌈ a 1  d i s t ( i ) − i  ⌉ + 1 
由于 d i s t ( i ) dist(i) d i s t ( i ) mod  a 1 = i \text{mod }a_1=i mod  a 1  = i 
⌊ x − i a 1 ⌋ − d i s t ( i ) − i a 1 + 1 = ⌊ x − d i s t ( i ) a 1 ⌋ + 1 \lfloor\frac{x-i}{a_1}\rfloor-\frac{dist(i)-i}{a_1}+1=\lfloor\frac{x-dist(i)}{a_1}\rfloor+1
 ⌊ a 1  x − i  ⌋ − a 1  d i s t ( i ) − i  + 1 = ⌊ a 1  x − d i s t ( i )  ⌋ + 1 
但是下面的代码还是用的原始公式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include  <bits/stdc++.h>  using  namespace  std;typedef  long  long  LL;typedef  pair<LL, int > PII;const  int  N = 15 , M = 500010 ;int  a[N], n;LL dist[M]; LL l, r; void  dijkstra ()      priority_queue<PII, vector<PII>, greater<PII>> heap;     bitset<M> st;     memset (dist, 0x3f , sizeof  dist);     dist[0 ] = 0 ;     heap.push ({0 , 0 });     while  (heap.size ()) {         int  t = heap.top ().second; heap.pop ();         if  (st[t]) continue ;         st[t] = true ;         for  (int  i = 1 ; i <= n; i++) {             int  to = (t + a[i]) % a[1 ], w = a[i];             if  (dist[to] > dist[t] + w) {                 dist[to] = dist[t] + w;                 heap.push ({dist[to], to});             }         }     } } LL query (LL x)   {    LL res = 0 ;     for  (int  i = 0 ; i < a[1 ]; i++)         if  (dist[i] <= x)             res += (x-i)/a[1 ] - (dist[i]-i+a[1 ]-1 )/a[1 ] + 1 ;     return  res; } int  main ()      scanf ("%d%lld%lld" , &n, &l, &r);     int  m = 0 ;     for  (int  i = 1 ; i <= n; i++) {         scanf ("%d" , &a[++m]);         if  (!a[m]) m--;     }     n = m;     dijkstra ();     return  !printf ("%lld\n" , query (r) - query (l-1 )); } 
[ABC077D] Small Multiple 
给定一个正整数 K K K K K K 2 ≤ K ≤ 10 5 2 \le K \le {10}^5 2 ≤ K ≤ 10 5 
题目链接:ABC077D 。
 
由于求 K K K mod  K \text{mod }K mod  K 0 0 0 f ( x ) f(x) f ( x ) x   m o d   K x \bmod K x mod K x x x 
首先 f ( 1 ) = 1 f(1)=1 f ( 1 ) = 1 ( + 1 ) ( + 1 ) ( × 10 ) ( + 1 ) (+1)(+1)(\times 10)(+1) ( + 1 ) ( + 1 ) ( × 10 ) ( + 1 ) ( × 10 ) (\times 10) ( × 10 ) 0 0 0 ( + 1 ) (+1) ( + 1 ) 1 1 1 
f ( i + 1 ) = min  { f ( i ) + 1 } f ( 10 i ) = min  { f ( i ) } f(i+1)=\min\{f(i)+1\}\\
f(10i)=\min\{f(i)\}
 f ( i + 1 ) = min { f ( i ) + 1 } f ( 10 i ) = min { f ( i )} 
直接写个 01BFS 或者 Dijkstra 都可以。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include  <bits/stdc++.h>  using  namespace  std;typedef  pair<int , int > PII;const  int  N = 100010 ;int  k, dist[N];bool  st[N];int  main ()      scanf ("%d" , &k);     priority_queue<PII, vector<PII>, greater<PII>> heap;     memset (dist, 0x3f , sizeof  dist);     dist[1 ] = 1 ;     heap.push ({1 , 1 });     while  (heap.size ()) {         int  t = heap.top ().second; heap.pop ();         if  (st[t]) continue ;         st[t] = true ;         int  to, w;         to = t * 10  % k, w = 0 ;         if  (dist[to] > dist[t] + w) {             dist[to] = dist[t] + w;             heap.push ({dist[to], to});         }         to = (t + 1 ) % k, w = 1 ;         if  (dist[to] > dist[t] + w) {             dist[to] = dist[t] + w;             heap.push ({dist[to], to});         }     }     return  !printf ("%d\n" , dist[0 ]); }