[BZOJ 2118] 墨墨的等式
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=2118
[算法]
首先找出Min{Ai}
对于0 - Min{Ai} - 1分别建一个点 , 分别表示一个同余类
若(i + Aj) % Min{Ai} = k , 则i向k连一条权值为Aj的边
从0号点开始单源最短路 , 那么就得到了模Min{Ai}为任意数的最小值
计算较为简单 , 不再赘述
时间复杂度 : O(Min{Ai}logMin{Ai}) (需要使用高效的Dijkstra + 堆优化算法)
[代码]
#include<bits/stdc++.h> using namespace std; const int MAXN = 13; const int MAXA = 5e5 + 10; typedef long long LL; const LL inf = 1e18; struct edge { int to , w , nxt; } e[MAXN * MAXA]; int n , mn , tot; LL Bmin , Bmax; int a[MAXN] , head[MAXA]; bool visited[MAXA]; LL dist[MAXA]; priority_queue< pair<LL , int> , vector< pair<LL , int> > , greater< pair<LL , int> > > q; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline void addedge(int u , int v , int w) { ++tot; e[tot] = (edge){v , w , head[u]}; head[u] = tot; } inline LL calc(LL x , LL y , LL z) { LL ret = x / y; if (x % y >= z && x >= 1) ++ret; return ret; } int main() { read(n); read(Bmin); read(Bmax); for (int i = 1; i <= n; i++) read(a[i]); mn = a[1]; for (int i = 2; i <= n; i++) if (a[i] < mn) mn = a[i]; for (int i = 0; i < mn; i++) { for (int j = 1; j <= n; j++) { addedge(i , (i + a[j]) % mn , a[j]); } } for (int i = 0; i < mn; i++) dist[i] = inf; dist[0] = 0; q.push(make_pair(0 , 0)); while (!q.empty()) { int cur = q.top().second; q.pop(); if (visited[cur]) continue; visited[cur] = true; for (int i = head[cur]; i; i = e[i].nxt) { int v = e[i].to , w = e[i].w; if (dist[cur] + w < dist[v]) { dist[v] = dist[cur] + w; q.push(make_pair(dist[v] , v)); } } } LL ans = 0; for (int i = 0; i < mn; i++) { LL l = max(dist[i] , Bmin) , r = Bmax; if (l > r) continue; ans += calc(r , mn , i) - calc(l - 1 , mn , i); } printf("%lld\n" , ans); return 0; }