2021牛客多校#5
B Boxes
这一题是求期望,对于hint的使用,我们发现我们要么一次不用,要么最多用一次,因为你用一次之后,你每次打开一个盒子,都会知道剩下盒子的情况,所以我们分别计算这两种方案然后取min即可。
1.不用hint:ans = Σw[i]
2.用hint:ans = c + Σsum[i] * (1/2)^(n-i) 其中sum[i]表示开前i个盒子(根据花费升序排列)所需的费用,(1/2)^(n-i)表示后面n-i个盒子里面颜色一致,那么就不用开了。
最后两种ans取一个min即可。
code
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
double sum[N], w[N];
double res, ans, c;
int main()
{
scanf("%d%lf", &n, &c);
for (int i = 1; i <= n; i ++ )
{
scanf("%lf", &w[i]);
res += w[i];
}
sort(w + 1, w + n + 1);
for (int i = 1; i <= n; i ++ ) sum[i] = sum[i - 1] + w[i];
ans += c;
for (int i = 1; i <= n - 1; i ++ )
ans += sum[i] * pow(0.5, n - i);
ans = min(ans, res);
printf("%.6f
", ans);
return 0;
}
K King of Range (单调队列)
朴素一点的思路,我们可以枚举右端点r,然后搜索[1, r - 1]寻找满足条件的最靠右的l,但是会t。
所以要优化,我们维护两个单调队列,一个是单调上升,一个是单调下降的队列,我们发现队列中不仅存的值是单调的,他们对应的下标也是单调的。以维护qm为例(单调下降队列),每次遍历到一个a[r],那么要拿a[r]与其队尾作比较,如果a[r] >= a[qm.back()] 说明此时队列这个值不仅小于a[r],而且相比于a[r]距离r更远,一定是劣于a[r]的。这样维护两个队列之后,我们不断的访问两个队列的对头,不断的将满足条件的两个队首里面下标较小的弹出,一步一步直到找到满足条件的下标最大的一个l,因为此时l满足条件,所以[1, l - 1]为左端点时也一定满足条件,那么此时以r为右端点的情况下的贡献就是l。
code
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
long long n, m;
long long a[N];
void solve()
{
int k;
long long ans = 0;
cin >> k;
int l = 0;//要找的最靠右的l
deque<int> qm, qn;//双端队列,方便维护
for (int r = 1; r <= n; r ++ )
{
//维护两个队列
while (qm.size() && a[r] >= a[qm.back()]) qm.pop_back(); qm.push_back(r);
while (qn.size() && a[r] <= a[qn.back()]) qn.pop_back(); qn.push_back(r);
//不断的访问队头元素,不断更新更优的l
while (qm.size() && qn.size() && a[qm.front()] - a[qn.front()] > k)
{
if (qm.front() > qn.front()) l = qn.front(), qn.pop_front();
else l = qm.front(), qm.pop_front();
}
//记录贡献
ans += 1ll * l;
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
while (m -- ) solve();
return 0;
}