ARC082F - Sandglass(思维)

ARC082 D - Sandglass

Solution

这题睡觉的时候 想了挺久的。

一段时间Δt\Delta tΔt内要么是让x+Δtx+\Delta tx+ΔtXXXminminmin,要么是让x−Δtx-\Delta txΔt000maxmaxmax

如果没有对边界取max/minmax/minmax/min,就是一个单纯的前缀和了。而加上对边界取max/minmax/minmax/min,对于没有撞到过边界的还是一个和之前一样的前缀和,但如果撞到过边界答案就会改变。

我们发现有这样一个性质:
对于[0,X][0,X][0,X]的这一个区间经过若干次操作之后,会存在一个前缀和000贴贴(指之后的值都和000一样),存在一个后缀和XXX贴贴(指之后的值都和XXX一样)。

这个性质的来源可以通过下列过程得到:

  • 初始有一个区间[L,R]=[0,X][L,R]=[0,X][L,R]=[0,X]表示还没有和000XXX贴贴的区间。
  • 若这一次是减的,[L′,R′]=[L−Δt,R−Δt][L',R']=[L-\Delta t,R-\Delta t][L,R]=[LΔt,RΔt],且L′<0L'<0L<0了,那么当前的[L′,0)[L',0)[L,0)这一段值就和000贴贴了,也就是说,初始区间中与000贴贴的一部分前缀的长度增加−L′-L'L
  • 若这一次是加的,[L′,R′]=[L+Δt,R+Δt][L',R']=[L+\Delta t,R+\Delta t][L,R]=[L+Δt,R+Δt],且R′>XR'>XR>X了,那么当前的(X,R′](X,R'](X,R]这一段值就和000贴贴了,也就是说,初始区间中与000贴贴的一部分前缀的长度增加R′−XR'-XRX
  • 注:这里为了方便不会让正好等于0,X0,X0,X的数和0,X0,X0,X贴贴。
  • (是不是和NOIP2020  T4NOIP2020\;T4NOIP2020T4的方法有异曲同工之妙?)

然后我们发现上述过程我们是可以直接模拟的,于是可以求出ai,taia_i,ta_iai,tai表示[0,ai)[0,a_i)[0,ai)最早在taita_itai时刻与000贴贴,同样bi,tbib_i,tb_ibi,tbi表示与XXX贴贴的我们也能求出来。

然后我们可以预处理000的变化和XXX的变化。
对于(x,y)(x,y)(x,y)这个询问,如果yyyxxx时刻之前和000XXX贴贴了,那就按照0/X0/X0/X之前预处理的答案算即可;如果没有,那么就表示yyyxxx时刻之前从来没有撞到过000XXX的边界,因此再求一个前缀和计算就可以了。

时间复杂度O(nlgn)O(nlgn)O(nlgn)

Code

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <ctime>
#include <cassert>
#include <string.h>
//#include <unordered_set>
//#include <unordered_map>
//#include <bits/stdc++.h>

#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second

using namespace std;

template<typename T>inline bool upmin(T &x,T y) { return y<x?x=y,1:0; }
template<typename T>inline bool upmax(T &x,T y) { return x<y?x=y,1:0; }

typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int,int> PR;
typedef vector<int> VI;

const lod eps=1e-11;
const lod pi=acos(-1);
const int oo=1<<30;
const ll loo=1ll<<62;
const int mods=998244353;
const int MAXN=600005;
const int INF=0x3f3f3f3f;//1061109567
/*--------------------------------------------------------------------*/
inline int read()
{
	int f=1,x=0; char c=getchar();
	while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
	while (c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
	return x*f;
}
int T[MAXN],numa=0,numb=0,s[MAXN],p0[MAXN],px[MAXN];
PR a[MAXN],b[MAXN];
signed main()
{
	int X=read(),n=read();
	for (int i=1;i<=n;i++) T[i]=read(); T[n+1]=INF+INF;
	int L=0,R=X;
	for (int i=1;i<=n+1;i++)
	{
		int t=T[i]-T[i-1];
		if (i&1) 
		{
			L-=t,R-=t;
			if (R<0) { numa++,a[numa]=MP(a[numa-1].fi+R-L+1,i); break; }
			else if (L<0) numa++,a[numa]=MP(a[numa-1].fi-L,i),L=0;
		}
		else
		{
			L+=t,R+=t;
			if (L>X) { numb++,b[numb]=MP(b[numb-1].fi+R-L+1,i); break; }
			else if (R>X) numb++,b[numb]=MP(b[numb-1].fi+R-X,i),R=X; 
		}
	}
	p0[0]=0,px[0]=X;
	for (int i=1;i<=n+1;i++)
	{
		int t=T[i]-T[i-1];
		if (i&1) s[i]=s[i-1]-t,p0[i]=max(p0[i-1]-t,0),px[i]=max(px[i-1]-t,0);
		else s[i]=s[i-1]+t,p0[i]=min(p0[i-1]+t,X),px[i]=min(px[i-1]+t,X);
	}
	int Case=read();
	while (Case--)
	{
		int x=read(),y=read(),p=lower_bound(T+1,T+n+2,x)-T-1,ans;
		if (y<a[numa].fi)
		{
			int t=lower_bound(a+1,a+numa+1,MP(y,-1))-a;
			if (T[a[t].se]>x) ans=y+s[p]+(x-T[p])*((p&1)?1:-1);
			else ans=p0[p]+(x-T[p])*((p&1)?1:-1);
		}
		else if (X-y<b[numb].fi)
		{
			int t=lower_bound(b+1,b+numb+1,MP(X-y,-1))-b;
			if (T[b[t].se]>x) ans=y+s[p]+(x-T[p])*((p&1)?1:-1);
			else ans=px[p]+(x-T[p])*((p&1)?1:-1);
		}
		upmin(ans,X),upmax(ans,0);
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值