gym101201J Shopping 二分+RMQ+数学性质
题目大意:
给出n个商品的价格,排成一列,q次查询,每次查询如果你有x的钱,从l格子走到r格子,每种商品有无数个,能买就买,最后还会剩多少钱。
思路:
每一次买都要找离自己最近的且买的起的商品,这样可以二分区间,用线段树(rmq问题,可以用st表)找到离自己最近且买得起的商品,然后不断的向r逼近,最后就是答案。
这个思路为什么不会超时的呢,因为可以想象,每次买完一个商品,你的剩余的钱最多也是这个商品价格的余数,而后面你买的起的商品价格肯定比这个小,所以稍微举几个例子就发现不会枚举几次的,有点像斐波那契数列的递减。(最坏的情况是商品价格为7,6,5,4,3,2,1,这样的时间复杂度也许会退化,但发现如果能走完这个过程,至少也要有28块钱,这个钱在买7的时候会直接用完,所以时间复杂度不会退化)。
借用了队友写的代码,st表解决RMQ真的好短。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
——愿为泰山而不骄
qq850874665~~