1 条题解
-
0
考虑当前加入一个新点该放在什么位置。设放在位置 ,左右两边距离其最近的点位置为 和 。则 一定要在 到 中间,即 等于 。
最直观的想法是将两点之间的段都放入一个大根堆中。每次加入一个点就取出堆顶的段并分成两段加入堆中。对于每次询问,我们考虑离线来做。每加入点判断能否更新答案。
该做法的时间复杂度是 ,无法通过。
考虑优化。因为每次加入一个点都会导致该段一分为二,使得段数巨大,所以考虑优化段数。我们发现将区间一分为二后左右两段长度相等,所以出现了很多相邻而长度相等的段。因此我们可以将这些段放在一起考虑。
具体来说,我们对于堆中的每个元素,维护若干个三元组 ,表示从位置 开始有连续的 个长度为 的段。那么每加入点时,当前三元组就可以被替换为 。每替换一次相当于加入 个点。将询问从小到大排序,能更新答案就更新即可。复杂度 。
细节部分, 要表现为分数形式 ,用结构体存起来,记得要开 __int128。
#include<bits/stdc++.h> using namespace std; long long n,X,z,a[1000010]; struct question{ long long num,id; }Q[100010]; template<typename T>inline void write(T x){ if(x<0)putchar('-'),x=(~x)+1; if(x>9)write(x/10); putchar(x%10^48); } struct number{ __int128 x,y; bool operator<(const number b)const{ return x*b.y<b.x*y; } number operator*(const number b)const{ number res; res.x=x*b.x,res.y=y*b.y; long long L=__gcd(res.x,res.y); return (number){res.x/L,res.y/L}; } number operator/(const number b)const{ return (number){x,y}*(number){b.y,b.x}; } number operator+(const number b)const{ number res; res.x=x*b.y+b.x*y,res.y=y*b.y; long long L=__gcd(res.x,res.y); return (number){res.x/L,res.y/L}; } }ans[100010]; struct node{ number len; long long st,cnt; bool operator<(const node b)const{ if(len.x!=b.len.x||len.y!=b.len.y)return len<b.len; else return st>b.st; } }; priority_queue<node>q; bool cmp(question x,question y){ return x.num<y.num; } int main(){ scanf("%lld%lld%lld",&n,&X,&z); for(int i=1;i<=n;++i){ scanf("%lld",&a[i]); if(i>=2)q.push((node){(number){a[i]-a[i-1],1},a[i-1],1}); } for(int i=1;i<=z;++i)scanf("%lld",&Q[i].num),Q[i].id=i; sort(Q+1,Q+z+1,cmp); long long idx=1,tot=0; while(idx<=z){ node u=q.top(); q.pop(); number l=u.len*(number){1,2}; while(idx<=z&&tot+u.cnt>=Q[idx].num){ ans[Q[idx].id]=(number){u.st,1}+l+((number){Q[idx].num-tot-1,1}*u.len);//求答案 idx++; } tot+=u.cnt; q.push((node){l,u.st,u.cnt<<1}); } for(int i=1;i<=z;++i)write(ans[i].x),putchar('/'),write(ans[i].y),puts(""); return 0; }
- 1
信息
- ID
- 7537
- 时间
- 7000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者