P7948 [✗✓OI R1] 前方之风 题解
蒟蒻的第一篇题解
题目传送门
Sol 0
暴力模拟+二分
然鹅赛后被 hack 掉了。
Sol 1
排序+后缀和
每次删数必定是从小到大删,考虑对原数组进行排序。对于需判断的一个数
可以维护一个后缀和,
复杂度
Sol 1.5
依照上面的思路,设第
若
若
可以发现,只需要将
将这个结论稍加扩展:对于排序后序列中的一个数
每次询问
复杂度
梅开二度
尽管在时间上并没有优化,但在算法简单了许多,只需循环上套个判断就可以了。
Sol 2
考虑对多次查询进行优化。
显然,若
其他大佬都是用的双指针,这里介绍另一种无脑的方法:将询问数组从大到小 sort 一遍,当某个
复杂度:
AC代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct xmh{ //%%%xmh
ll val,num,ans;
}k[100010];
ll n,q;
ll a[100010];
ll add[100010];
double avg[100010];
inline bool cmp1(xmh a,xmh b){
return a.val > b.val;
}
inline bool cmp2(xmh a,xmh b){
return a.num < b.num;
}
void init(){
memset(a,0,sizeof(a)); //初始化
memset(add,0,sizeof(add));
memset(avg,0,sizeof(avg));
memset(k,0,sizeof(k));
n = 0,q = 0;
k[0].val = -1,k[0].num = -1,k[0].ans = -1;
scanf("%lld%lld",&n,&q);
for(ll i = 1; i <= n; ++i)
scanf("%lld",&a[i]);
for(ll i = 1; i <= q; ++i){
scanf("%lld",&k[i].val);
k[i].num = i;
}
sort(a+1,a+1+n); //对原数组及查询数组排序
sort(k+1,k+1+q,cmp1);
add[n] = a[n];
for(ll i = n-1; i >= 1; --i){ //后缀和
add[i] += add[i+1] + a[i];
}
for(ll i = 1; i <= n; ++i){ //平均数
avg[i] = 1.0 * add[i] / (n-i+1);
}
return;
}
void print(){
sort(k+1,k+1+q,cmp2);
for(ll i = 1; i <= q; ++i)
printf("%lld ",k[i].ans);
putchar('\n');
}
void process(){
ll p = 1,sum = n;
for(ll i = 1; i <= n && p <= q;){
while(a[i] < avg[i] - k[p].val)++i; //比较 a[i] 与 avg[i] - k[p] 的关系
k[p].ans = n-i+1; //满足条件即记录 ans
p++; //多次查询优化
}
print();
return;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
init();
process();
}
return 0;
}
upd:
2021.11.28 修改代码里的一些小错误(两个主函数?)
2021.12.17 将 Sol1.5 里的证明修正了一下
2021.12.18 又修了点排版错误淦