[题解]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\)。那么我们将整个过程看作两段,答案就是:
左边是从 \(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\),则有转移:
总时间 \(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;
}
浙公网安备 33010602011771号