1 条题解

  • 0
    @ 2026-4-23 18:03:16

    一个 O(m+qlogm)O(m+q\log m) 的做法。

    首先每次询问 (x,y)(x,y) 只需要考察 tiyti+Lt_i\le y\le t_i+L 的顾客,不妨假定这些顾客是 tl,tl+1,,trt_l,t_{l+1},\cdots,t_r

    然后容易发现对于时刻 x+y,2x+y,3x+y,x+y,2x+y,3x+y,\cdots,厨师都会做好一个面包,于是相当于将这些时刻与顾客进行匹配。

    不妨考虑 Hall 定理,最大匹配数是 SmaxTS(TE(T))|S|-\max\limits_{T\subseteq S}(|T|-|E(T)|),令 SS 为顾客的集合,则 E(T)E(T) 只会取决于最大的那个 tt,显然 TT 一定取的是一个顾客的前缀,即答案是 $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$。

    于是我们的问题变成了求 maxi=lr(ixti)\max\limits_{i=l}^r (ix-t_i)

    区间一次函数最值问题显然可以使用线段树维护凸壳 / 李超树做到 O((m+q)logm)O((m+q)\log m)O(mlog2m+qlogm)O(m\log^2m+q\log m) 等,但是 m2×106m\le 2\times10^6,不妨认为出题人想让我们做到 O(m+qlogm)O(m+q\log m)

    考虑询问的特征,因为 l,rl,r 锁定的是一个 ti[yL,y]t_i\in[y-L,y] 的区间,所以将询问按左端点排序,则右端点同样是单调的。

    将询问离线,同时维护一个队列,显然我们只需要在加点和删点时维护队列中所有点的凸壳。

    注意到两个栈就可以模拟一个队列,具体的,维护两个栈 z1,z2z1,z2,队列从队头到队尾的顺序是 z1z1 栈顶到栈底的顺序再拼上 z2z2 栈底到栈顶的顺序。每次入队时就直接在 z2z2 中进栈,出队时考虑若 z1z1 为空就将 z2z2 的整个栈删空并倒着插入进 z1z1,然后弹掉 z1z1 的栈顶。

    于是这个队列被我们使用了两个栈维护,且这两个栈插入的点横坐标也具有单调性。

    现在相当于插入点、撤销上一次插入、并维护凸壳。

    但有一个问题是凸壳是使用单调栈维护的,而单调栈是有势能的,无法进行撤销操作,是不是意味着无法维护?

    但其实仔细分析双栈模拟队列的操作,对于 z1z1,只有他为空了才会发生一连串的进栈操作,对于 z2z2,他每次出栈都会直接将栈弹空。

    于是我们维护的栈只会进行一连串的插入,然后进行一连串的撤销直到栈为空,如此循环往复。

    所以我们可以直接对每个点存一下他进栈的时候弹掉了哪些点,将一个点撤销出栈的时候将其进栈时弹掉的点加回来就好了。

    对于一个点,其在一个栈中只会进栈 22 次,出栈 22 次,所以整个复杂度是 O(m+qlogm)O(m+q\log m)

    因为场上写的比较急眼,代码非常丑:

    #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
    上传者