1 条题解
-
0
一个 的做法。
首先每次询问 只需要考察 的顾客,不妨假定这些顾客是 。
然后容易发现对于时刻 ,厨师都会做好一个面包,于是相当于将这些时刻与顾客进行匹配。
不妨考虑 Hall 定理,最大匹配数是 ,令 为顾客的集合,则 只会取决于最大的那个 ,显然 一定取的是一个顾客的前缀,即答案是 $r-l+1-\max\limits_{i=l}^r(i-l+1-\lfloor\frac{t_i+L-y}x\rfloor)$,稍微推一下可以变成 $r-\max\limits_{i=l}^r(i-\lfloor\frac{t_i+L-y}x\rfloor)$,然后可以变成 $r-\lceil\max\limits_{i=l}^r(i-\frac{t_i+L-y}x)\rceil$,然后变成 $r-\lceil\frac{\max\limits_{i=l}^r(ix-t_i)-L+y}x\rceil$。
于是我们的问题变成了求 。
区间一次函数最值问题显然可以使用线段树维护凸壳 / 李超树做到 或 等,但是 ,不妨认为出题人想让我们做到 。
考虑询问的特征,因为 锁定的是一个 的区间,所以将询问按左端点排序,则右端点同样是单调的。
将询问离线,同时维护一个队列,显然我们只需要在加点和删点时维护队列中所有点的凸壳。
注意到两个栈就可以模拟一个队列,具体的,维护两个栈 ,队列从队头到队尾的顺序是 栈顶到栈底的顺序再拼上 栈底到栈顶的顺序。每次入队时就直接在 中进栈,出队时考虑若 为空就将 的整个栈删空并倒着插入进 ,然后弹掉 的栈顶。
于是这个队列被我们使用了两个栈维护,且这两个栈插入的点横坐标也具有单调性。
现在相当于插入点、撤销上一次插入、并维护凸壳。
但有一个问题是凸壳是使用单调栈维护的,而单调栈是有势能的,无法进行撤销操作,是不是意味着无法维护?
但其实仔细分析双栈模拟队列的操作,对于 ,只有他为空了才会发生一连串的进栈操作,对于 ,他每次出栈都会直接将栈弹空。
于是我们维护的栈只会进行一连串的插入,然后进行一连串的撤销直到栈为空,如此循环往复。
所以我们可以直接对每个点存一下他进栈的时候弹掉了哪些点,将一个点撤销出栈的时候将其进栈时弹掉的点加回来就好了。
对于一个点,其在一个栈中只会进栈 次,出栈 次,所以整个复杂度是 。
因为场上写的比较急眼,代码非常丑:
#include<bits/stdc++.h> #define int long long using namespace std; int n,m,len,q; int a[2000005]; const int inf=0x3f3f3f3f3f3f3f3f; struct poly { bool f; int c; stack<pair<int,int>> z; deque<pair<int,int>> q; vector<pair<int,int>> v[2000005]; void add(int x,int y) { z.push({x,y}); c++; while(q.size()>=2&&((y-q.back().second)*(q.back().first-q[q.size()-2].first)>(q.back().second-q[q.size()-2].second)*(x-q.back().first))!=f) v[c].push_back(q.back()),q.pop_back(); q.push_back({x,y}); reverse(v[c].begin(),v[c].end()); } pair<int,int> undo() { pair<int,int> p=z.top(); z.pop(); q.pop_back(); for(pair<int,int> p:v[c]) q.push_back(p); v[c].clear(); c--; return p; } int query(int k) { if(q.empty()) return (f?inf:-inf); int l=1,r=q.size()-1; while(l<=r) { int mid=l+r>>1; if((q[mid].second-q[mid-1].second>k*(q[mid].first-q[mid-1].first))!=f) l=mid+1; else r=mid-1; } return q[r].second-q[r].first*k; } }z1,z2; struct node { int x,y,id; bool operator < (const node a) const { return y<a.y; } }c[400005]; int ans[400005]; signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n>>m>>len>>q; for(int i=1;i<=m;i++) cin>>a[i]; for(int i=1;i<=q;i++) { cin>>c[i].x>>c[i].y; c[i].id=i; } sort(c+1,c+q+1); int l=1,r=1; z1.f=0,z2.f=1; for(int i=1;i<=q;i++) { while(r<=m&&a[r]<=c[i].y) z1.add(r,-(a[r]+len)),r++; while(l<=m&&a[l]<c[i].y-len) { if(!z2.c) while(z1.c) { pair<int,int> p=z1.undo(); z2.add(-p.first,-p.second); } z2.undo(); l++; } int cur=max((max(z1.query(-c[i].x),-z2.query(-c[i].x))+c[i].y+c[i].x-1)/c[i].x-l+1,0ll); ans[c[i].id]=r-l-cur; } for(int i=1;i<=q;i++) cout<<ans[i]<<"\n"; }
- 1
信息
- ID
- 11189
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者