P14827 吃吃饱

P14827 吃吃饱

前言

赛事想到这个思路,但是有事所以没时间写完,赛后因为弱智错误卡了三天。

思路

考虑每一个点的位置都可以通过有效点(传送门和起点)和该点的距离算出来,然后考虑直接一个有效点从前面的有效点转移过来需要 \(O(n^2)\),本来想拿到 \(\color{green}97pts\) 就走,然后发现这样做每个询问也要枚举每一个有效点,所以需要 \(O(n^2+qn)\)。拿不到高分于是我发现每一个到顶上的距离是两个一次函数组成的一个凹的锥形,然后我想到拿李超线段树维护,这样就可以获得高分。过了一会我想到:每一个有效点的转移同样可以从前面有效点在李超上的值转移过来。于是就做完了,我可以获得 \(\color{green} 100pts\) 皆大欢喜!

赛后写了很久,最后问题出在李超线段树,在离散化后会有一些缝隙,可以让区间重叠或者动态开点。

ACcode

#include <bits/stdc++.h>
using namespace std;
#define freop(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define int long long
#define ld long double
#define re register
#define endl '\n'
#define inf 0x7f7f7f7f
#define lb(x) (x&-x)
#define pii pair<int,int>
#define fir first
#define sec second
const int N=5e5+2,M=N<<5;
int n,m,stx,q,x[N][2];

class Lichao_Segment_Tree{
	int le,ri,dx,ans;
	int tot,ut=1,lx[N<<2],rx[N<<2],x[N<<2]={0},k[N<<2]={inf};
	struct node{
		int ls,rs,id;
	}tr[M];
	inline int js(int a,int b){return x[a]*b+k[a];}
	void cg(int u,int l,int r,int dx){
		int &x=tr[u].id,mid=l+r>>1;
		if(!x){x=dx;return ;}
		if(js(dx,mid)<js(x,mid)) swap(dx,x);
		if(js(dx,l)<js(x,l)){
			if(!tr[u].ls) tr[u].ls=++ut;
			cg(tr[u].ls,l,mid,dx);
		}
		if(js(dx,r)<js(x,r)){
			if(!tr[u].rs) tr[u].rs=++ut;
			cg(tr[u].rs,mid+1,r,dx);
		}
	}
	void add(int u=1,int l=-1e9,int r=1e9){
		if(le<=l&&r<=ri){cg(u,l,r,tot);return ;}
		int mid=l+r>>1;
		if(le<=mid){
			if(!tr[u].ls) tr[u].ls=++ut;
			add(tr[u].ls,l,mid);
		}
		if(ri>mid){
			if(!tr[u].rs) tr[u].rs=++ut;
			add(tr[u].rs,mid+1,r);
		}
	}
	void ask(int u=1,int l=-1e9,int r=1e9){
		ans=min(ans,js(tr[u].id,dx));
		if(l==r||!u) return ;
		int mid=l+r>>1;
		if(dx<=mid) ask(tr[u].ls,l,mid);
		else ask(tr[u].rs,mid+1,r);
	}
	public:
	inline void change(int l,int r,int inx,int ink){
		x[++tot]=inx,k[tot]=ink,lx[tot]=l,rx[tot]=r,le=l,ri=r,add();}
	inline void insert(int x,int y){
		change(-inf,x-1,-1,y+x);
		change(x,inf,1,y-x);
	}
	inline int query(int x){ans=inf,dx=x,ask();return ans;}//询问到最后一行的答案 
}lst; 

signed main(){
	cin.tie(0)->ios::sync_with_stdio(false);
	cout.tie(0);
//	freop("in");
//	freopen("in.in","r",stdin);
//↑以上为初始化↑
//------------------------------
	cin >> n >> q >> stx;
	for(int i = 1;i<=n;++i)
		cin >> x[i][0] >> x[i][1];
	lst.insert(stx,0);
	for(int i = 1,t0,t1;i<=n;++i){//处理每一行的答案
		t0=lst.query(x[i][0]);
		t1=lst.query(x[i][1]);
		lst.insert(x[i][1],t0);
		lst.insert(x[i][0],t1);
	}
	while(q--){
		int in;cin >> in;
		cout << lst.query(in)+n-1 << endl;
	}
	return 0;
}
/*
考虑从每一个点(传送门和起点)到最上面一行的答案都是一个锥形的下凸的函数

于是每一个点的答案可以从下面的传送门转移,具体来说:

到每一个传送门的答案实际上是该点头顶的距离,然后直接把这个值直接赋给队友传送门 ,因为到上面的距离都一样,而传送不消耗韭菜盒子 
*/
posted @ 2026-10-21 21:11  elon_giao  阅读(4)  评论(0)    收藏  举报