收获
IOI 庄园有 \(N\) 个员工,\(M\) 棵苹果树种在湖岸。湖的周长为 \(L\) 米。
一开始员工 \(i\) 位于从湖的最北端向顺时针方向前进 \(A_i\) 米处,所有 \(A_i\) 互异。苹果树 \(j\) 生长在从湖的最北端向顺时针方向前进 \(B_j\) 米处,所有 \(B_j\) 互异。
每棵苹果树最多长一个苹果,收获后 \(C\) 秒会长出一个新的。时刻 \(0\) 时,所有的苹果树上都有一个苹果。员工从时刻 \(0\) 开始从各自的地点以 \(1\text{m/s}\) 的速度顺时针前进,遇到成熟的苹果就将其摘下(若到达时刚长出苹果,也要摘下),摘苹果的时间忽略不计。
现给出 \(Q\) 个询问,第 \(k\) 次询问员工 \(V_k\) 在时刻 \(T_k\) 结束时一共收获到几个苹果。
对于 \(100\%\) 的数据,\(1 \leq N,M \leq 2\times 10^5\),\(N+M \leq L \leq 10^9\),\(1 \leq C \leq 10^9\),\(1 \leq Q \leq 2\times 10^5\)。
题解
果树和人是相对运动的,因为是对人做询问,所以可以考虑让人不动,果树运动。这样的好处是可以把人的结构固定下来,便于用数据结构维护。
具体来讲,对于所有\(i\in[1,n]\),我们从第\(i\)个人向逆时针方向第一个到他的距离\(\geq C\)的人\(j\)连边。表示某棵果树被\(i\)摘掉后,下一个摘的人是\(j\)。边权是这两次采摘的时间间隔。
对于每棵果树,向它逆时针方向的第一个人连边。表示它的果子第一次是被这个人摘到的。边权是这个人到这棵果树的距离。
因为每个点都有且仅有一条出边,于是,我们得到了一个基环内向树森林。其中,每棵果树是一个叶子,其他节点都是人。开始时,果树会从它所在的叶子出发,经过每条边需要一定的时间。我们要回答\(Q\)次询问,问在某一时刻之前某个节点共被果树经过了多少次(同一棵果树如果多次经过同一节点,则每次都要被算在答案里)。
先把询问离线下来。给每个节点开一个vector,存关于这个节点的询问。
基环树森林里的每棵基环树显然相互独立。我们对分别对每棵基环树计算答案。
对于一棵基环树,我们先断掉它环上的一条边。然后分两种情况讨论:
果树到达被询问节点时,之前没有经过被断掉的这条边。
果树到达被询问节点时,之前经过了被断掉的这条边。注意,因为是在环上,所以有可能多次经过。
对于第一种情况,答案显然是树上(基环树断掉一条边后会变成普通的树)某个子树内,深度不超过某个值的果树数量。用DFS序把“子树”转化成一段区间,问题变为简单的二维数点问题。具体来说,我们把所有的果树和询问放在一起,按深度排序。每次加入一个点,或询问一个区间内的点数。可以用树状数组维护。
对于第二种情况。我们先把每棵果树移动到被断掉的这条边的终点。对于第\(i\)棵果树,设它移动到这个点所花费的时间为\(t_i\)。设要回答的询问时间为\(T\)。则对答案的贡献是\(\sum_{t_i\leq T}\frac{T-t_i}{len}\)。其中\(len\)是基环树的环长。这里的除法取整是一个细节。如果余数大于等于被断掉的边的终点到被询问的节点的距离,则上取整,否则下去整。设这个距离为\(d\),则我们也可以形式化地把这个式子写作:
\[\sum_{t_i\leq T}(\lfloor\frac{T-t_i}{len}\rfloor+[(T-t_i)\bmod len>d\bmod len])=\sum_{t_i\leq T}\lfloor\frac{T-t_i+len-d}{len}\rfloor \]
其实这也是一个二维数点问题。先把果树和询问一起按时间排序。我们可以用两个变量\(num,sum\),分别记录已经扫描到的果树的数量,和果树的\(\lfloor\frac{t_i}{len}\rfloor\)之和。对于某个查询\(x=T+len-d\),先令答案等于\(\lfloor\frac{x}{len}\rfloor\cdot num-sum\)。这样会多计算的是\(t_i\bmod len\)大于\(x\bmod len\)的这些果树(每棵这样的果树会使答案被多算\(1\))。我们事先把所有余数离散化一下,然后在树状数组上做单点修改、后缀和查询即可。
时间复杂度\(O(n\log n)\)。
CO int N=2e5+10;
int A[N],B[N];
pair<int,int> fa[N];
vector<pair<int,int> > to[N],val[N],que[N];
pair<int,int> ban;
int L[N],R[N],dfn,dep[N];
struct event {int o,x,t;};
vector<event> F,G;
void dfs(int x,int fa){
L[x]=++dfn;
for(CO pair<int,int>&v:val[x])
F.push_back({0,L[x],dep[x]+v.second});
for(CO pair<int,int>&e:to[x])if(e.first!=fa){
if(x==ban.first and e.first==ban.second) continue;
dep[e.first]=dep[x]+e.second;
dfs(e.first,x);
}
R[x]=dfn;
for(CO pair<int,int>&q:que[x]){
F.push_back({-q.first,L[x]-1,dep[x]+q.second});
F.push_back({q.first,R[x],dep[x]+q.second});
}
}
namespace bit{
int n,S[2*N];
IN void init(int n){
bit::n=n,fill(S,S+n+1,0);
}
void insert(int p,int v){
for(int i=p;i<=n;i+=i&-i) S[i]+=v;
}
int query(int p){
int ans=0;
for(int i=p;i;i-=i&-i) ans+=S[i];
return ans;
}
}
int64 ans[N];
signed main(){
int n=read<int>(),m=read<int>();
int L=read<int>(),C=read<int>();
function<int(int,int)> dist=[&](int x,int y)->int{ // clockwise
return x<=y?y-x:L+y-x;
};
for(int i=1;i<=n;++i) read(A[i]);
for(int i=1;i<=n;++i){
int A0=((A[i]-C)%L+L)%L;
fa[i].first=upper_bound(A+1,A+n+1,A0)-A-1; // find the next
if(!fa[i].first) fa[i].first=n;
fa[i].second=dist(A[fa[i].first],A0)+C;
to[fa[i].first].push_back({i,fa[i].second});
}
for(int i=1;i<=m;++i){
read(B[i]);
int x=upper_bound(A+1,A+n+1,B[i])-A-1;
if(!x) x=n;
val[x].push_back({i,dist(A[x],B[i])});
}
int q=read<int>();
for(int i=1;i<=q;++i){
int x=read<int>(),T=read<int>();
que[x].push_back({i,T});
}
for(int i=1;i<=n;++i)if(!::L[i]){
static bool vis[N];
int x=i;
for(;!vis[x];x=fa[x].first) vis[x]=1;
ban={fa[x].first,x};
dfn=0,F.clear(),dfs(x,0);
sort(F.begin(),F.end(),[&](CO event&a,CO event&b)->bool{
return a.t!=b.t?a.t<b.t:abs(a.o)<abs(b.o);
});
bit::init(dfn);
for(CO event&f:F){
if(f.o==0) bit::insert(f.x,1);
else if(f.o<0) ans[-f.o]-=bit::query(f.x);
else ans[f.o]+=bit::query(f.x);
}
G.clear();
for(CO event&f:F)if(f.o==0)
G.push_back({0,0,f.t+fa[x].second}); // t_i
int p=fa[x].first,len=0;
while(1){
for(CO pair<int,int>&q:que[p])if(q.second>=len)
G.push_back({q.first,0,q.second-len}); // T-d
len+=fa[p].second,p=fa[p].first;
if(p==fa[x].first) break;
}
sort(G.begin(),G.end(),[&](CO event&a,CO event&b)->bool{
return a.t!=b.t?a.t<b.t:abs(a.o)<abs(b.o);
});
vector<int> tmp;
for(event&g:G){
if(g.o>0) g.t+=len; // T+len-d
tmp.push_back(g.t%len);
}
sort(tmp.begin(),tmp.end());
tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
for(event&g:G)
g.x=lower_bound(tmp.begin(),tmp.end(),g.t%len)-tmp.begin()+1;
bit::init(tmp.size());
int64 sum=0,num=0;
for(CO event&g:G){
if(g.o==0){
bit::insert(g.x,1);
sum+=g.t/len,++num;
}
else{
ans[g.o]+=g.t/len*num-sum;
ans[g.o]-=bit::query(tmp.size())-bit::query(g.x);
}
}
}
for(int i=1;i<=q;++i) printf("%lld\n",ans[i]);
return 0;
}