收获
IOI 庄园有 (N) 个员工,(M) 棵苹果树种在湖岸。湖的周长为 (L) 米。
一开始员工 (i) 位于从湖的最北端向顺时针方向前进 (A_i) 米处,所有 (A_i) 互异。苹果树 (j) 生长在从湖的最北端向顺时针方向前进 (B_j) 米处,所有 (B_j) 互异。
每棵苹果树最多长一个苹果,收获后 (C) 秒会长出一个新的。时刻 (0) 时,所有的苹果树上都有一个苹果。员工从时刻 (0) 开始从各自的地点以 (1 ext{m/s}) 的速度顺时针前进,遇到成熟的苹果就将其摘下(若到达时刚长出苹果,也要摘下),摘苹果的时间忽略不计。
现给出 (Q) 个询问,第 (k) 次询问员工 (V_k) 在时刻 (T_k) 结束时一共收获到几个苹果。
对于 (100\%) 的数据,(1 leq N,M leq 2 imes 10^5),(N+M leq L leq 10^9),(1 leq C leq 10^9),(1 leq Q leq 2 imes 10^5)。
题解
https://www.cnblogs.com/dysyn1314/p/12582983.html
果树和人是相对运动的,因为是对人做询问,所以可以考虑让人不动,果树运动。这样的好处是可以把人的结构固定下来,便于用数据结构维护。
具体来讲,对于所有(iin[1,n]),我们从第(i)个人向逆时针方向第一个到他的距离(geq C)的人(j)连边。表示某棵果树被(i)摘掉后,下一个摘的人是(j)。边权是这两次采摘的时间间隔。
对于每棵果树,向它逆时针方向的第一个人连边。表示它的果子第一次是被这个人摘到的。边权是这个人到这棵果树的距离。
因为每个点都有且仅有一条出边,于是,我们得到了一个基环内向树森林。其中,每棵果树是一个叶子,其他节点都是人。开始时,果树会从它所在的叶子出发,经过每条边需要一定的时间。我们要回答(Q)次询问,问在某一时刻之前某个节点共被果树经过了多少次(同一棵果树如果多次经过同一节点,则每次都要被算在答案里)。
先把询问离线下来。给每个节点开一个vector,存关于这个节点的询问。
基环树森林里的每棵基环树显然相互独立。我们对分别对每棵基环树计算答案。
对于一棵基环树,我们先断掉它环上的一条边。然后分两种情况讨论:
-
果树到达被询问节点时,之前没有经过被断掉的这条边。
-
果树到达被询问节点时,之前经过了被断掉的这条边。注意,因为是在环上,所以有可能多次经过。
对于第一种情况,答案显然是树上(基环树断掉一条边后会变成普通的树)某个子树内,深度不超过某个值的果树数量。用DFS序把“子树”转化成一段区间,问题变为简单的二维数点问题。具体来说,我们把所有的果树和询问放在一起,按深度排序。每次加入一个点,或询问一个区间内的点数。可以用树状数组维护。
对于第二种情况。我们先把每棵果树移动到被断掉的这条边的终点。对于第(i)棵果树,设它移动到这个点所花费的时间为(t_i)。设要回答的询问时间为(T)。则对答案的贡献是(sum_{t_ileq T}frac{T-t_i}{len})。其中(len)是基环树的环长。这里的除法取整是一个细节。如果余数大于等于被断掉的边的终点到被询问的节点的距离,则上取整,否则下去整。设这个距离为(d),则我们也可以形式化地把这个式子写作:
其实这也是一个二维数点问题。先把果树和询问一起按时间排序。我们可以用两个变量(num,sum),分别记录已经扫描到的果树的数量,和果树的(lfloorfrac{t_i}{len} floor)之和。对于某个查询(x=T+len-d),先令答案等于(lfloorfrac{x}{len} floorcdot num-sum)。这样会多计算的是(t_imod len)大于(xmod len)的这些果树(每棵这样的果树会使答案被多算(1))。我们事先把所有余数离散化一下,然后在树状数组上做单点修改、后缀和查询即可。
时间复杂度(O(nlog 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
",ans[i]);
return 0;
}