[BZOJ2118] 墨墨的等式(最短路)
阿新 • • 發佈:2017-10-10
getch val light ont etc 選擇 clu sdi con
傳送門
好神啊。。
需要用非負數個a1,a2,a3...an來湊出B
可以知道,如果一個數x能被湊出來,那麽x+a1,x+a2.......x+an也都能被湊出來
那麽我們只需要選擇a1~an中任意一個的a,可以求出在%a下的每個數最小需要多少才能湊出來
這樣我們選擇一個最小的a,速度更快,令m=min(a[k]) 1 <= k <= n
然後建模,i向(i+a[j])%m連一條權值為a[j]的邊
跑一邊最短路就可以了
然後需要求Bmin~Bmax中的解
只需要ans(Bmax)-ans(Bmin)即可
註意a[i]==0的點。。。。
#include <queue> #include <cstdio> #include <cstring> #include <iostream> #define N 6000001 #define LL long long using namespace std; int n, cnt; int head[N], to[N], next[N]; LL L, R, ans, dis[N], m = ~(1 << 31), a[21], val[N]; bool vis[N]; queue <int> q; inline LL read() { LL x = 0, f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == ‘-‘) f = -1; for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - ‘0‘; return x * f; } inline void add(int x, int y, LL z) { to[cnt] = y; val[cnt] = z; next[cnt] = head[x]; head[x] = cnt++; } inline void spfa() { int i, u, v; for(i = 0; i < m; i++) dis[i] = 1e13; q.push(0); dis[0] = 0; while(!q.empty()) { u = q.front(); vis[u] = 0; q.pop(); for(i = head[u]; ~i; i = next[i]) { v = to[i]; if(dis[v] > dis[u] + val[i]) { dis[v] = dis[u] + val[i]; if(!vis[v]) { vis[v] = 1; q.push(v); } } } } } inline LL query(LL x) { int i; LL ans = 0; for(i = 0; i < m; i++) if(dis[i] <= x) ans += (x - dis[i]) / m + 1; return ans; } int main() { LL x, y; int i, j; n = read(); L = read(); R = read(); memset(head, -1, sizeof(head)); for(i = 1; i <= n; i++) { a[i] = read(); if(!a[i]) { i--, n--; continue; } m = min(m, a[i]); } for(i = 0; i < m; i++) for(j = 1; j <= n; j++) add(i, (i + a[j]) % m, a[j]); spfa(); printf("%lld\n", query(R) - query(L - 1)); return 0; }
[BZOJ2118] 墨墨的等式(最短路)