day 5 模拟赛 3
模拟赛 3
T1 游戏
有 n 种游戏,第 i 个价格为 \(c_i\) 元,你第 0 天有 a 元钱,每天(不含第 0 天)可以获得 b 元钱,每天至多购买 1 种游戏,求买完所有游戏所需的最小天数。
显然,反正每天获得的钱都固定,如果你能买尽量就买,因为一天不能买多个,所以我们把游戏的价格排序,从小到大地贪心购买,复杂度 \(O(n)\),数据较弱。
T2 Median
定义一个含 m 个数列的中位数为把数列从小到大排序后的第 \(m/2+1\) 个数(向下取整)。
求一个长度为 n 的序列中 \(n(n+1)/2\) 个子区间中中位数所构成数列的中位数。
部分分:直接最暴力的枚举、排序,复杂度 \(O(n^3\log n)\)。-------------------------------30分
用队顶堆优化,每次移动 r 时更新答案,复杂度 \(O(n^2)\)。-------------------------------60分
正解:本题是经典套路,考虑二分答案,若答案是 mid,那么它是最终答案需要满足有 \(n(n+1)/2/2+1\) 个区间求出的中位数比 mid 大,这样 mid 在最终序列中才处在中间位置。
考虑如何求有多少个中位数比 mid 大,这里也用一个经典的转化,设比 mid 大的数为 1,比 mid 小的数为 -1,那么一个区间的中位数大于等于 mid 就等价于这个 0-1 区间的和大于等于 0。
我们考虑求出这个 0-1 数列的前缀和 s,发现区间和大于等于 0,又等价于 \(s_{l-1}\ge s_r\),所以我们可以建一个权值树状数组,把值存到下标中,存的内容是该下标的数量,枚举一次原数组,每次查询 \(1~a_i\) 中树状数组的和,也就是满足上面式子的个数,之后把新枚举的值加入树状数组,方便后续查询。
时间复杂度 \(O(n\log^2n)\)。-------------------------------100分
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,cnt,a[100005],b[100005],c[100005],s[100005];
int tr[200005];
int lowbit(int x){
return x&(-x);
}
void update(int x,int k){
for(;x<=n*2+5;x+=lowbit(x))tr[x]+=k;
}
int query(int x){
int ans=0;
for(;x;x-=lowbit(x))ans+=tr[x];
return ans;
}
signed main(){
freopen("median.in","r",stdin);
freopen("median.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
if(n<=100){
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
for(int k=i;k<=j;k++)b[k-i]=a[k];
sort(b,b+j-i+1);
c[++cnt]=b[(j-i+1)/2];
}
}
sort(c+1,c+cnt+1);
cout<<c[cnt/2+1];
return 0;
}
int k=n+5;
int l=0,r=1e9;
while(l<r){
int mid=(l+r+1)/2;
memset(tr,0,sizeof tr);
s[0]=0;
for(int i=1;i<=n;i++){
if(a[i]>=mid)b[i]=1;
else b[i]=-1;
s[i]=s[i-1]+b[i];
}
int ans=0;
update(s[0]+k,1);
for(int i=1;i<=n;i++){
ans+=query(s[i]+k);
update(s[i]+k,1);
}
if(ans>=n*(n+1)/2/2+1)l=mid;
else r=mid-1;
}
cout<<l;
return 0;
}
T3 秋游
有 n 个未知的正整数 aᵢ(≤ bᵢ),分别去掉每个数后,剩下 n-1 个数的 gcd 会得到 n 个值。问在所有可能的 aᵢ 中,这 n 个 gcd 最多能有几种不同的数。
n 个数的 gcd 实际上就是分别分解质因数,求每个质数指数最小数的乘积。
最优的构造方式如下:
- 所有出现过的质因子是最小的 k 个质数(2, 3, 5, 7, 11, …)这是显然的,不然容易超出 \(b_i\) 的限制。
- 有一个数包含所有这些质因子(乘积最大)
- 另外 k 个数,第 i 个数缺少第 i 个质因子(也就是乘积去掉一个质数)这样才能保证第一条。
我们发现,当 \(k>15\) 时,前 k 个质数的乘积 \(2*3*5*...\) 已经超过 long long,所有只需要枚举 k 在 1 到 \(\min(n-1,15)\) 之间,判断是否能满足 \(b_i\) 的限制即可。
构造出来的数列也就是 \(S,S/2,S/3,S/5,...\),共 \(k+1\) 项,其中 S 为前 k 个质数的积。
那么如何判断?我们贪心,把构造出来的 a 和给出的 b 排序,依次匹配,就是最有可能的方案。
复杂度 \(O(n\log n+nk)\)。-------------------------------100分
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,b[200005],a[20];
int prim[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
signed main(){
freopen("trip.in","r",stdin);
freopen("trip.out","w",stdout);
cin>>n;
for(int i=1;i<=n;i++) cin>>b[i];
sort(b+1,b+n+1);
int ans=1;
for(int k=1;k<=min(n-1,(int)15);k++){
int p=1;
for(int i=1;i<=k;i++) p*=prim[i];
int cnt=0;
a[++cnt]=p;
for(int i=1;i<=k;i++) a[++cnt]=p/prim[i];
sort(a+1,a+cnt+1);
bool flag=1;
for(int i=1;i<=cnt;i++){
if(b[i]<a[i]){
flag=0;
break;
}
}
if(flag)ans=k+1;
}
cout<<ans;
return 0;
}
浙公网安备 33010602011771号