@dxbdly
2021-02-08T11:15:03.000000Z
字数 3276
阅读 178
信息学——模拟赛
考场题面有锅,然而最后出题人也没补好,所以这题就咕咕了吧。
考虑DP。
设:表示前 本书的最小值。
枚举点 ,表示 到 为最后一层。
则有方程:
一个技巧: 从 开始倒着枚举,好处是我们可以一遍扫一遍存下 和 非常方便
(当然用ST表和前缀和维护也可以)
//The code is from dawn_sdy#include<bits/stdc++.h>#define INF INT_MAXusing namespace std;inline int read() {int x = 0;char c = getchar();bool f = 0;while(!isdigit(c)) f |= (c == '-'), c = getchar();while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();return f ? -x : x;}const int maxn = 2005;int n, l;struct node {int h, w;}book[maxn];int f[maxn], maxx, sum;int main(){// freopen("bookshelf.in", "r", stdin);// freopen("bookshelf.out", "w", stdout);n = read(), l = read();for(register int i = 1; i <= n; ++i) book[i].h = read(), book[i].w = read();for(register int i = 1; i <= n; ++i) {f[i] = INF, maxx = sum = 0;for(register int j = i; j >= 1; --j) {maxx = max(maxx, book[j].h), sum += book[j].w;if(sum > l) break;f[i] = min(f[i], f[j - 1] + maxx);}}printf("%d", f[n]);return 0;}
先吐槽一句:题面依旧有问题,最后是理解std改的题面。。。
不过此题思路极其巧妙,值得一做。
首先考虑一定只存在速度快的奶牛追上速度慢的奶牛,那么如果按照速度排序,奶牛 的贡献只与有关
考虑奶牛 追上奶牛 的贡献:
首先算出时间
与 之间的关系。
设:, 其中 为整数部分, 为小数部分。
则 ,
我们考虑的影响
那么就可以将上式展开:
考虑到小数部分不好处理,我们将小数转换成余数,记余数为 。
则可以发现 就是 满足 且 的个数,即可以看成 数组逆序对的对数,而逆序对采用树状数组维护即可,复杂度。
//The code is from dawn_sdy#include<bits/stdc++.h>#define int long longusing namespace std;inline int read() {int x = 0;char c = getchar();bool f = 0;while(!isdigit(c)) f |= (c == '-'), c = getchar();while(isdigit(c)) x = (x * 10) + (c ^ 48), c = getchar();return f ? -x : x;}const int maxn = 1e5 + 5, maxl = 1e6;int n, le, c, ans, sum, maxx;struct node {int x, y, v;}point[maxn];inline bool operator <(node a, node b) {return a.x != b.x ? a.x < b.x : a.y < b.y;}namespace Binary_Index_Tree {int Tree[maxl << 1 + 5];inline int lowbit(int x) { return x & (-x); }inline void Update(int x, int num) {while(x <= maxl) { Tree[x] += num, x += lowbit(x); }}inline int Query(int x) {int res = 0;while(x) { res += Tree[x], x -= lowbit(x); }return res;}}using namespace Binary_Index_Tree;signed main() {n = read(), le = read(), c = read();for(register int i = 1; i <= n; ++i) point[i].v = read(), maxx = max(maxx, point[i].v);for(register int i = 1; i <= n; ++i) point[i].x = point[i].v * le / maxx, point[i].y = (point[i].v * le) % maxx;sort(point + 1, point + n + 1);for(register int i = 1; i <= n; ++i) {ans += point[i].x * (i - 1) - sum - (Query(maxl) - Query(point[i].y)), sum += point[i].x;if(point[i].y) Update(point[i].y, 1);}printf("%lld", ans);return 0;}
一道非常好的题,需要记住的是 的转化方式,以及将小数部分对应余数,进而转化为逆序对的思想。