gym101201J Shopping 二分+RMQ+數學性質
阿新 • • 發佈:2018-12-09
題目大意:
給出n個商品的價格,排成一列,q次查詢,每次查詢如果你有x的錢,從l格子走到r格子,每種商品有無數個,能買就買,最後還會剩多少錢。
思路:
每一次買都要找離自己最近的且買的起的商品,這樣可以二分割槽間,用線段樹(rmq問題,可以用st表)找到離自己最近且買得起的商品,然後不斷的向r逼近,最後就是答案。
這個思路為什麼不會超時的呢,因為可以想象,每次買完一個商品,你的剩餘的錢最多也是這個商品價格的餘數,而後面你買的起的商品價格肯定比這個小,所以稍微舉幾個例子就發現不會列舉幾次的,有點像斐波那契數列的遞減。(最壞的情況是商品價格為7,6,5,4,3,2,1,這樣的時間複雜度也許會退化,但發現如果能走完這個過程,至少也要有28塊錢,這個錢在買7的時候會直接用完,所以時間複雜度不會退化)。
借用了隊友寫的程式碼,st表解決RMQ真的好短。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=200010; int n, q; ll a[N]; ll dp[N][20]; int mm[N]; void init(int n, ll b[]) { mm[0] = -1; for (int i = 1; i <= n; ++i) { mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1View Code] + 1: mm[i - 1]; dp[i][0] = b[i]; } for (int j = 1; j <= mm[n]; ++j) for (int i = 1; i + (1 << j) - 1 <= n; ++i) dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); } ll query(int l, int r) { int k = mm[r - l + 1]; return min(dp[l][k], dp[r - (1<< k) + 1][k]); } int main() { while (scanf("%d%d", &n, &q) != EOF) { for (int i = 1; i <= n; ++i) scanf("%lld", a + i); init(n, a); ll v; for (int i = 1, l, r; i <= q; ++i) { scanf("%lld%d%d", &v, &l, &r); while (r - l >= 0) { int ql = l, qr = r, tar = -1; while (qr - ql >= 0) { int mid = (ql + qr) >> 1; if (query(ql, mid) <= v) { qr = mid - 1; tar = mid; } else ql = mid + 1; } if (tar == -1) break; v %= a[tar]; l = tar + 1; } printf("%lld\n", v); } } return 0; }