【题解】[JOISC2020] 収穫
一个环上有 \(N\) 个人,\(M\) 个苹果树,每一秒每个人顺时针走一米,每隔 \(C\) 秒长一个苹果,\(Q\) 次询问,每次询问第 \(V_i\) 个人在 \(T_i\) 秒内能摘多少苹果。
不难发现如果相邻两个人间隔 \(\ge C\) ,那么上一个摘的苹果这个人一定能摘到,否则上一个人摘得苹果这个人肯定摘不到。
所以不难完成第一步转化,对于每个人 \(i\) 向它后面距离 \(\ge C\) 米的第一个人连边,记作 \(p_i\)。表示第 \(i\) 个人吃了一个苹果,那么下一个在这里吃苹果的人是 \(p_i\) 。注意可以自己向自己连边。
每个人只有一条出边,那么这就是基环内向树森林。
基环树比较套路的做法是断开一条边使得变成一棵树,然后计算不经过断开边和经过断开边的贡献。
不经过断开边,那么就是子树查询深度在一定区间内的苹果树,经典二维数点,直接离线树状数组即可。
经过断开边,我们可以先将所有苹果树移动到根节点,设移动的距离为 \(x_i\) 。
那么对于一个询问 \((V_i,T_i)\) ,设环上根节点到 \(V_i\) 的距离为 \(y_i\) ,如果 \(V_i\) 就是根节点,则距离设为环的长度 $Len $。注意这里是基环树的环长,不是湖的周长。
那么答案显然为 \(\sum \max\{0,\left\lfloor\dfrac{T_i-y_i-x_j}{Len}\right\rfloor+1\}\)。
这个式子非常丑,考虑计算偏序,令 \(T_i-y_i\ge x_j\) ,那么可以去掉 \(\max\) 。再把 \(1\) 塞进去,得到一个好看一点的式子。
$Len $ 是一个定值,可以式子可以写作 \(\sum\limits_{w_i\ge x_j}\left\lfloor\dfrac{w_i-x_j}{k}\right\rfloor\) 。
直接拆开做即可,本质还是二维数点,离线树状数组即可。时间复杂度 \(\mathcal{O}((N+M)\log (N+M))\) 。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
#define int long long
using namespace std;
int n,m,L,C,a[N],ps[N],p[N],u[N],v[N],h[N],tot,idx,sz[N],dfn[N],cir[N],ro[N],b[N],T,ans[N],o[N],Q,d[N];
struct edge{int to,nxt,val;}e[N];
void add(int x,int y,int z){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;e[tot].val=z;}
struct seg{
int c[N];
inline void add(int x,int y){for(;x<=max(n,m);x+=x&-x)c[x]+=y;}
inline void ins(int l,int r,int val){add(l,val);add(r+1,-val);}
inline int ask(int x){int sum=0;for(;x;x-=x&-x)sum+=c[x];return sum;}
}cc,cr;
vector<int>rt;
inline int g(int x){if(x==1)return n;return x-1;}
inline int vl(int x){if(x==1)return L-a[n]+a[1];return a[x]-a[x-1];}
void dfs(int x,int col){
v[x]=col;
if(v[p[x]]){
if(v[p[x]]==col){rt.push_back(x);return;}
add(p[x],x,u[x]);return;
}
add(p[x],x,u[x]);dfs(p[x],col);
}
void calc(int x,int Rt){
dfn[x]=++idx;sz[x]=1;ro[x]=Rt;
for(int i=h[x];i;i=e[i].nxt)d[e[i].to]=d[x]+e[i].val,calc(e[i].to,Rt),sz[x]+=sz[e[i].to];
}
void ins(int x,int len){
cir[x]=len;if(!cir[p[x]])ins(p[x],len+u[x]);
}
struct node{
int x,d,op;
node(int X=0,int D=0,int O=0){x=X,d=D,op=O;}
bool operator<(const node o)const{return d<o.d;}
};
vector<node>qa,qb,qd[N];vector<int>qc[N];
void init(){
int cc=C%L,j=n,len=0,ad=C/L*L,cn=0;
pre(i,n,1){
while(len<cc){len+=vl(j);j=g(j);}
p[i]=j;u[i]=len+ad;len-=vl(i);
}
rep(i,1,n)if(!v[i])dfs(i,++cn);
for(int i=0;i<(int)rt.size();i++)calc(rt[i],rt[i]),ins(p[rt[i]],u[rt[i]]);
rep(i,1,m){
if(ps[i]<a[1]){
qa.push_back(node(n,ps[i]+L-a[n]+d[n],0)),
qc[ro[n]].push_back(ps[i]+L-a[n]+d[n]);
}
else {
int j=lower_bound(a+1,a+n+1,ps[i])-a-1;
qa.push_back(node(j,ps[i]-a[j]+d[j],0));
qc[ro[j]].push_back(ps[i]-a[j]+d[j]);
}
}
sort(qa.begin(),qa.end());
for(int i=0;i<(int)qa.size();i++)if(i==0||qa[i].d!=qa[i-1].d)b[++T]=qa[i].d;
for(int i=0;i<(int)qa.size();i++)qa[i].d=lower_bound(b+1,b+T+1,qa[i].d)-b;
scanf("%lld",&Q);
rep(op,1,Q){
int x,y;scanf("%lld%lld",&x,&y);
int l=lower_bound(b+1,b+T+1,d[x])-b,r=upper_bound(b+1,b+T+1,d[x]+y)-b-1;
if(l<=r){
qb.push_back(node(x,l-1,-op));
qb.push_back(node(x,r,op));
}
if(cir[x])qd[ro[x]].push_back(node(x,y-cir[x]+cir[ro[x]],op));
}
}
void solve(){
sort(qb.begin(),qb.end());int j=0,tp;
for(int i=0;i<(int)qb.size();i++){
while(j<(int)qa.size()&&qa[j].d<=qb[i].d)cr.add(dfn[qa[j++].x],1);
if(qb[i].op>0)ans[qb[i].op]+=cr.ask(dfn[qb[i].x]+sz[qb[i].x]-1)-cr.ask(dfn[qb[i].x]-1);
else ans[-qb[i].op]-=cr.ask(dfn[qb[i].x]+sz[qb[i].x]-1)-cr.ask(dfn[qb[i].x]-1);
}
for(int w=0;w<(int)rt.size();w++){
int k=rt[w],len=cir[k];j=tp=T=0;int cnt=0,sum=0;
sort(qc[k].begin(),qc[k].end());
sort(qd[k].begin(),qd[k].end());
for(int i=0;i<(int)qc[k].size();i++)o[++tp]=qc[k][i]%len;
sort(o+1,o+tp+1);
rep(i,1,tp)if(i==1||o[i]!=o[i-1])b[++T]=o[i];
for(int i=0;i<(int)qd[k].size();i++){
while(j<(int)qc[k].size()&&qc[k][j]<=qd[k][i].d){
assert(lower_bound(b+1,b+T+1,qc[k][j]%len)-b>=1);
cnt++,sum+=qc[k][j]/len,cc.add(lower_bound(b+1,b+T+1,qc[k][j]%len)-b,1),j++;
}
ans[qd[k][i].op]+=cnt*(qd[k][i].d/len)-sum-(cnt-cc.ask(upper_bound(b+1,b+T+1,qd[k][i].d%len)-b-1));
}
while(j)j--,cc.add(lower_bound(b+1,b+T+1,qc[k][j]%len)-b,-1);
}
}
signed main(){
scanf("%lld%lld%lld%lld",&n,&m,&L,&C);
rep(i,1,n)scanf("%lld",&a[i]);
rep(i,1,m)scanf("%lld",&ps[i]);
init();solve();rep(i,1,Q)printf("%lld\n",ans[i]);
return 0;
}