1. 程式人生 > >gym101201J Shopping 二分+RMQ+數學性質

gym101201J Shopping 二分+RMQ+數學性質

題目傳送門

題目大意:

  給出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 - 1
] + 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; }
View Code