题目链接
一道毒瘤题
/*
直接贴题解,代码附注释
显然所有询问都要经过至少∑d,只需要考虑除了∑d之外的等待红灯的时间。
将所有询问的时间模g+r,并按时间用set维护。
那么对于每个红灯,在set中可以找出1到2个区间,将里面所有的询问暴力取出,添加一个新点作为等到绿灯后的询问放入。
那么询问与新点之间构成了一棵树结构,每个询问实际的答案为它到根路径上所有点的答案之和。
时间复杂度O(nlogn)
*/
#include<bits/stdc++.h>
#define ll long long
#define P pair<ll,int>
#define sit set<P>::iterator
#define N 150010
using namespace std;
int n,f[N],q[N],tot;
ll g,r,m,d[N],x,w[N];
bool v[N];
set<P>T;
ll dfs(int x){
if(v[x])return w[x];
v[x]=true;
return w[x]+=dfs(f[x]);
}//树形dp(裸)
void solve(ll L,ll R,ll K){
int cnt=0;
for(sit it=T.lower_bound(P(L,0));it!=T.end()&&it->first<=R;it=T.lower_bound(P(L,0))){
q[cnt++]=it->second;
w[it->second]+=K-it->first;//加上等待红灯的时间
T.erase(it);
}//暴力找点连边
if(!cnt)return;//如果没有找到就退出
T.insert(P(K%m,++tot));//插点
for(int i=0;i<cnt;i++)f[q[i]]=tot;//记父亲
}
void calc(ll L){
L=(m-L+g)%m;//L变红左时间
ll R=L+r-1,K=R+1;//R变红右时间 K变绿时间
solve(L,min(R,m-1),K);//防止实际中L<sum%m,所以取min
if(R>=m)solve(0,R-m,K-m);//算出上述取min的情况还有半段没有计算,K-m是因为其实多算了一段
}
int main(){
//freopen("race.in","r",stdin);
//freopen("race.out","w",stdout);
scanf("%d%lld%lld",&n,&g,&r);
m=g+r;
for(int i=0;i<=n;i++)scanf("%lld",&d[i]);
int Q;scanf("%d",&Q);
for(int i=1;i<=Q;i++){
scanf("%lld",&w[i]);
T.insert(P(w[i]%m,i));
}
ll sum=0;tot=Q;
for(int i=0;i<=n;i++){
sum+=d[i];
if(i<n)calc(sum%m);
}
for(int i=1;i<=Q;i++)
printf("%lld\n",dfs(i)+sum);
return 0;
}