3 条题解
-
1
孩子们,调代码真有趣
第一问是结论,非常得简单。
如果不行,我们就双指针。
对了,如果你是 for 套 while ,千万不要忘了更新 mid。
#include<bits/stdc++.h> #define R(x) x=read() #define int long long #define N 100005 using namespace std; inline int read() { int x=0,y=1; char e=getchar(); while(e<'0'||e>'9') { if(e=='-')y=-1; e=getchar(); } while(e>='0'&&e<='9') { x=(x<<1)+(x<<3)+(e-'0'); e=getchar(); } return x*y; } int n,m,mon; int x[N],s[N]; int ans=1; signed main() { R(n),R(m),R(mon); for(int i=1; i<=n; ++i) { R(x[i]); } sort(x+1,x+1+n); int sum=0; for(int i=1; i<=n; ++i) { sum+=abs(x[n/2+1]-x[i]); } if(sum<=mon) { cout<<sum<<"\n"; return 0; } for(int i=1; i<=n; ++i) { s[i]=s[i-1]+x[i]; } int l=1,r=1; while(l<=n&&r<=n){ int mid=(r+l+1)/2; if(r<n&&s[r+1]-s[mid]-x[mid]*(r+1-mid)+x[mid]*(mid-l)-(s[mid-1]-s[l-1])<=mon)++r; else ++l,++r; ans=max(ans,r-l+1); } cout<<ans; return 0; } -
0
第一问还是比较简单的,假设开会地点左边有3个牛舍,右边有2个牛舍,那么开会地点每往左移一个单位,总路程-3+2=-1,也就是要尽可能让左右两边牛舍数量相同,自然就选在中位数的位置。
如果钱不够,考虑使用双指针,在过程中不断计算总花费,如果过多就左端点右移,注意要及时更新mid。 code:
#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,m,a[N],maxx=0; long long ans,s,h=0,c[N];//记得开long long!!! int main(){ //freopen("meeting.in","r",stdin); //freopen("meeting.out","w",stdout); ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m>>s; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+1+n); for(int i=1;i<=n;i++){ c[i]=a[i]-a[i-1]; } for(int i=2;i<=n/2+1;i++) ans+=(i-1ll)*c[i]; for(int i=n;i>=n/2+2;i--) ans+=(n-i+1ll)*c[i]; if(ans<=s){ cout<<ans; }else{ int f=1,r,mid; for(r=2;r<=n;r++){ mid=(f+r)>>1; h+=a[r]-a[mid]; while(h>s){ f++; mid=(f+r)>>1; h-=(a[mid]-a[f-1]); if(f==r) h=0; } maxx=max(maxx,r-f+1); } cout<<maxx; } return 0; } -
0
这道题你需要先知道一个性质:
在中位数的位置召集奶牛,一定是最优的
将 排序,设开会的地点在 处, 左侧有 头奶牛,右侧有 头奶牛。若 ,则每右移1,距离之和就会减小 。同理,若 ,则左移更优。因此,当 时为最优。
因此,预处理的代码如下
long long calc(int l,int r){ if (l==r) return 0ll; long long mid=(l+r)>>1, tot=l-r+1; long long res=(sum[r]-sum[mid])-(sum[mid-1]-sum[l-1])-(tot&1?0:a[mid]); return res; } //在main()中 if (calc(1,n)<=s){ printf("%lld",calc(1,n)); return 0; }
可是,怎么去找一个区间使其合法呢?
第一眼想到的是暴力枚举每一个区间。但是这样太劣了
注意到如果有一个区间是合法的,那么以后就没有必要遍历比它小的区间了
因此可以用双指针解决,:
-
如果区间合法,把右指针右移
-
如果区间不合法,把两个指针同时右移
便有AC代码声,此处无声胜有声
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; long long n,m,s,a[100005],sum[100005],ans; long long calc(int l,int r){ if (l==r) return 0ll; long long mid=(l+r)>>1, tot=r-l+1; long long res=(sum[r]-sum[mid])-(sum[mid-1]-sum[l-1])-(tot&1?0:a[mid]); return res; } int main(){ scanf("%lld%lld%lld",&n,&m,&s); for (int i=1;i<=n;i++){ scanf("%lld",a+i); } sort(a+1,a+n+1); for (int i=1;i<=n;i++){ sum[i]=sum[i-1]+a[i]; } if (calc(1,n)<=s){ printf("%lld",calc(1,n)); return 0; } long long l=1,r=1; while (l<n && r<n){ // cerr<<"---"<<l<<" "<<r<<" "<<calc(l,r+1)<<"\n"; if (calc(l,r+1)<=s){ r++; ans=max(ans,r-l+1); } else{ l++; r++; } } printf("%lld",ans); return 0; } -
- 1
信息
- ID
- 58
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- (无)
- 递交数
- 95
- 已通过
- 13
- 上传者