9.30 美妙的购物

题意

\(n\)个物品,每个的权值为\(a_i\)

定义正整数\(v\)为美丽的,当且仅当我们可以选取若干个物品使得其权值之和落在区间\([v,2v]\)

求有多少个美丽的数

\(n\leq 10^5,a_i\leq 10^9\)


解法

没想出正解,交的暴力还CE了。。菜的真实

可以考虑补集转换,也就是求出不美丽数,再用总数减去美丽数的个数

我们把每个物品转化为一个区间,这个区间所覆盖的数都是美丽数

(遇到这种值域极大的区间问题,就别想什么差分前缀和了,尝试用左右端点来表示一个区间)

我们可以把同时选择两个物品看做合并它们的两个区间

设此时有两个物品\(u,v(u<v)\),其代表的区间分别是\([\frac{u}{2},u],[\frac{v}{2},v]\)

若这两个区间相交,则合并出的新区间为\([\frac{u}{2},u+v]\),此时并没有不美丽数产生

若不相交,合并出的新区间同样是\([\frac{u}{2},u+v]\),但是此时有\(\frac{v}{2}-u\)个不美丽数产生(两者合并后的区间一定不会覆盖到这里,之后的区间也就更不会了)

可以发现,区间\(u,v\)合并后形成的新区间\(w\),既可以代表\(u\),也可以代表\(v\),也可以代表\(u+v\),因而这种计算方法是有结合律的

我们把区间按照右端点排序,计算不美丽数的个数即可


代码

#include <cstdio>
#include <cctype>
#include <algorithm>

using namespace std;

const int N = 1e5 + 10;

int read();

int n;
int a[N];

int main() {
    
    n = read();
    for (int i = 1; i <= n; ++i)  a[i] = read();
    
    sort(a + 1, a + n + 1);
    
    long long sum = 0, ans = 0, l = 0;
    for (int i = 1; i <= n; ++i) {
        l = (a[i] + 1) / 2;
        ans += max(0LL, l - sum - 1);
        sum += a[i];
    }
    
    printf("%lld\n", sum - ans);
    
    return 0;
}

int read() {
    int x = 0, c = getchar();
    while (!isdigit(c))  c = getchar();
    while (isdigit(c))   x = x * 10 + c - 48, c = getchar();
    return x;
}

猜你喜欢

转载自www.cnblogs.com/VeniVidiVici/p/11610083.html