数据读取
题解:二分答案。用h[]储存磁头位置,p[]储存需要读取的位置。
在(0,2e10)内找到一个最小值mid使得每个磁头移动mid步能够扫完所有需要读取的位置。
寻找mid的时候,由三种情况。
一 :此时h[i]-p[j]>mid,(即磁头到所需扫描位置大于mid时,说明此时mid比最大步数要小),那么应在(mid+1,r)范围内继续寻找。
二:在不符合一的时候,并且p[j]<h[i],(即所需读取位置在磁头左边)则磁头有两种移动方案:
由于磁头与左边需要读取的位置的距离为一定值,可先计算出来k=(h[i]-p[j])
1:先往左扫描需要扫描的地方,再向右移。此时扫描到的最大位置=p[j]+k;
2: 先往右扫描所需扫描的地方,再向左移。此时扫描到的最大位置=h[i]+k/2;
此时两种情况取一最大值maxn即为移动mid步时磁头所能到达的最大位置。
三:在不符合一的时候,并且p[j]>=h[i],(即所读取的位置在磁头右边)则磁头所能到达的最大位置maxn=p[j]+mid;
然后根据磁头所能到达的最大位置更新下一个所需读取的位置。若位置j>m时,说明此时mid值能够使得磁头读取完所有需要读取的位置。用ans储存下此时的mid,继续在(l,mid-1)内查找是否有更小值能够满足条件。
#include<cstdio> #include<iostream> #define N 100100 #define ll long long using namespace std; int n,m; ll ans; ll h[N],p[N]; void erfen(ll l,ll r) { if (l>r) return ; bool ff=0; ll mid=(l+r)>>1,maxn; for (int i=1,j=1;i<=n;i++) { if (h[i]-p[j]>mid) break;//若此时的磁头与此时需要扫描的位置大于mid,则最大步数应大于mid if (p[j]<h[i]) { ll k=mid-(h[i]-p[j]); maxn=max(p[j]+k,h[i]+(k>>1));//注意">>"的运算级比+还低,要加括号 } else maxn=h[i]+mid; while (p[j]<=maxn&&j<=m) j++; if (j>m)//此时磁头能够扫完所有需要扫描的位置 { ff=1; break; } } if (ff) ans=mid,erfen(l,mid-1); else erfen(mid+1,r); } int main() { freopen("data.in","r",stdin); freopen("data.out","w",stdout); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) cin>>h[i];//注意在windows系统下,用scanf("%lld")读取相当于读取int类型的数 //由于noip考试是在linx系统下进行,此时用scanf读入无问题 for (int i=1;i<=m;i++) cin>>p[i]; ll rr=2e10; erfen(0,rr); cout<<ans<<endl; fclose(stdin); fclose(stdout); return 0; }
I'm so lost but not afraid ,I've been broken and raise again