[题解]test1117 T4 上学/school

题意简述

有一条道路,Koishi 初始位于 \(0\) 位置,位置 \(1,2,\dots,n\) 处各有一个路口,Koishi 想经过这些路口到达 \(n+1\) 处。

从位置 \(i\) 到位置 \(i+1\) 所需要的时间为 \(a_i(i\in[0,n])\)

另外,每个路口设有红绿灯。所有红绿灯在 \(0\) 时刻为绿色,\(g\) 秒后变为红色,\(r\) 秒后变为绿色,以此类推。

Koishi 到达一个路口时,若是绿灯则可以直接通过,若是红灯则需要等待到绿灯才能通过(若到达某一路口时灯的状态正好发生改变,则视到达路口时灯的颜色为其改变后的颜色)。

\(q\) 次询问,每次给定 \(x\),求 Koishi 从 \(x\) 时刻出发到达 \(n+1\) 的时刻。

  • \(n,q\le 10^5\)
  • \(g,r,a_i,t\le 10^9\)

思路

\(n,q\le 10^3\)

模拟走路的过程即可,总时间 \(O(nq)\),期望 \(60\rm pts\)

while(q--){
	int c,d;
	cin>>c;
	for(int i=0;i<=n;i++){
		c+=a[i],d=c%(g+r);
		if(i!=n&&d>=g) c+=g+r-d;
	}
	cout<<c<<"\n";
}

\(n,q\le 10^5\)

下文记:

  • 起步位置为 \(0\),目标位置为 \(n+1\)
  • \(t_0\) 为绿灯时间,\(t_1\) 为红灯时间,\(t=t_0+t_1\),即一个红绿循环的时间。
  • \(p[i]=\sum_{j=0}^{i-1} a[j]\),即从 \(0\) 走到 \(i\) 不记等红绿灯的时间。

我们发现,中途遇到红灯后,接下来相当于在下一个绿灯的起始点起步。

如果我们能预处理出 \(f[i]\) 表示在一个绿灯的起始点从 \(i\) 起步,到达 \(n+1\) 的时间;并对于询问 \(z\),能找到在时刻 \(z\)\(0\) 起步,遇到的第一个红灯 \(x\)。那么我们将整个过程看作两段,答案就是:

\[\left\lceil\dfrac{z+p[x]}{t}\right\rceil\times t+f[x] \]

左边是从 \(0\) 走到 \(x\) 的时间加上等待红灯的时间,右边是从 \(x\) 走到 \(n+1\) 的时间。


我们先考虑如何找到在时刻 \(z\)\(0\) 起步的第一个红灯。

即寻找最小的 \(i\) 使得存在 \(x\in[t_0,t-1]\) 使得 \((p[i]+z)\equiv x\pmod t\)

即寻找最小的 \(i\) 使得存在 \(x\in[t_0-z,t-1-z]\),使得 \(p[i]\equiv x\pmod t\)

可以开一个 \([0,t-1]\) 的线段树(动态开点),把所有 \(p[i]\bmod t\) 处都与 \(i\)\(\min\);查询相当于区间 \(\min\)


我们再考虑如何预处理 \(f\)

原理是类似的,对于每个 \(i\),将 \(i+1,i+2,\dots,n+1\) 丢到刚才的线段树里(初始为空),这样可以通过区间 \(\min\) 找到在时刻 \(0\)\(i\) 起步的第一个红灯 \(x\),则有转移:

\[f[i]=\left\lceil\dfrac{p[x]-p[i]}{t}\right\rceil\times t+f[x] \]


总时间 \(O((n+q)\log V)\),期望 \(100\rm pts\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5,inf=0x3f3f3f3f3f3f3f3f;
int n,t0,t1,t,q,a[N],f[N],p[N],rt;
struct SEG{
	int idx,lc[N<<5],rc[N<<5],mn[N<<5];
	inline void init(){memset(mn,0x3f,sizeof mn);}
	inline int clone(int x){return mn[++idx]=mn[x],lc[idx]=lc[x],rc[idx]=rc[x],idx;}
	inline void upd(int x){mn[x]=min(mn[lc[x]],mn[rc[x]]);}
	inline void chp(int &x,int a,int v,int l,int r){
		x=clone(x);
		if(l==r) return mn[x]=min(mn[x],v),void();
		int mid=(l+r)>>1;
		if(a<=mid) chp(lc[x],a,v,l,mid);
		else chp(rc[x],a,v,mid+1,r);
		upd(x);
	}
	inline void chp(int a,int v){chp(rt,a%t,v,0,t-1);}
	inline int qry(int x,int a,int b,int l,int r){
		if(a<=l&&r<=b) return mn[x];
		int mid=(l+r)>>1,ans=inf;
		if(a<=mid) ans=min(ans,qry(lc[x],a,b,l,mid));
		if(b>mid) ans=min(ans,qry(rc[x],a,b,mid+1,r));
		return ans;
	}
	inline int qry(int a,int b){
		if(b-a>=t) return mn[rt];
		a=(a%t+t)%t,b=(b%t+t)%t;
		if(a>b) return min(qry(rt,a,t-1,0,t-1),qry(rt,0,b,0,t-1)); 
		return qry(rt,a,b,0,t-1);
	}
}seg;
inline int solve(int z){
	int x=seg.qry(t0-z,t-1-z);
	if(x==inf||x==n+1) return z+p[n+1];
	return f[x]+(z+p[x]+t-1)/t*t;
}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>t0>>t1,t=t0+t1;
	seg.init();
	for(int i=0;i<=n;i++){
		cin>>a[i];
		p[i+1]=p[i]+a[i];
	}
	for(int i=n+1;i;i--){
		seg.chp(p[i],i);
		int x=seg.qry(p[i-1]+t0,p[i-1]+t-1);
		if(x==inf||x==n+1) f[i-1]=p[n+1]-p[i-1];//走到 n+1 不需要等灯
		else f[i-1]=f[x]+(p[x]-p[i-1]+t-1)/t*t;
	}
	cin>>q;
	while(q--){
		int z;cin>>z;
		cout<<solve(z)<<"\n";
	}
	return 0;
}

posted @ 2025-11-17 21:36  Sinktank  阅读(122)  评论(0)    收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2025 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.