LOJ3278 收获

收获

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\)

题解

https://www.cnblogs.com/dysyn1314/p/12582983.html

果树和人是相对运动的,因为是对人做询问,所以可以考虑让人不动,果树运动。这样的好处是可以把人的结构固定下来,便于用数据结构维护。

具体来讲,对于所有\(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;
}

posted on 2020-04-13 18:24  autoint  阅读(170)  评论(0编辑  收藏  举报

导航