[PKUSC2018]星际穿越

[PKUSC2018]星际穿越

题目大意:

有一排编号为\(1\sim n\)\(n(n\le3\times10^5)\)个点,第\(i(i\ge 2)\)个点与\([l_i,i-1]\)之间所有点有双向边。\(q(q\le3\times10^5)\)次询问,每次对于\(l_i,r_i,x_i\),求\(\frac{\sum_{y=l_i}^{r_i}dist(x_i,y)}{r_i-l_i+1}\)

思路:

首先可以得到一个基本结论,从\(x_i\)出发到\(y\)的最短路中,一定存在至少一种满足路径上有且仅有第一步是向右走的,或者直接往左走。那么我们不妨对于每一个点\(x\),求出\(x\)右侧\(l_i\)最小的\(i=min[x]\),此时\(i\)的覆盖范围一定包含了\(x\)。让\(x\)\(i\)连边就得到了一个树形结构。在树上每个结点建立主席树维护原图每个点到树上对应结点的距离。

询问时对于\(l_i,r_i,x_i\),若区间\([l_i,r_i]\)内的结点都与\(x_i\)有连边,则答案就是\(r_i-l_i+1\)。否则那些在\(x_i\)连边范围外的那些点到\(x_i\)的距离,就是主席树上到\(min[x_i]\)的距离\(+1\)。到\(min[x_i]\)的距离可以主席树上询问,剩下的\(+1\)一并计算到\(r_i-l_i+1\)中即可。

时间复杂度\(\mathcal O(n\log n)\)

源代码:

#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
	register char ch;
	while(!isdigit(ch=getchar()));
	register int x=ch^'0';
	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
	return x;
}
const int N=3e5+1,SIZE=N*30;
int left[N],min[N],par[N];
class SegmentTree {
	private:
		struct Node {
			int left,right,tag,sum;
		};
		Node node[SIZE];
		int sz,new_node(const int &p) {
			node[++sz]=node[p];
			return sz;
		}
	public:
		int root[N];
		void modify(int &p,const int &b,const int &e,const int &l,const int &r) {
			p=new_node(p);
			if(b==l&&e==r) {
				node[p].tag++;
				return;
			}
			node[p].sum+=r-l+1;
			const int mid=(b+e)>>1;
			if(l<=mid) modify(node[p].left,b,mid,l,std::min(mid,r));
			if(r>mid) modify(node[p].right,mid+1,e,std::max(mid+1,l),r);
		}
		int query(const int &p,const int &b,const int &e,const int &l,const int &r) {
			if(!p) return 0;
			int ans=node[p].tag*(r-l+1);
			if(b==l&&e==r) return ans+node[p].sum;
			const int mid=(b+e)>>1;
			if(l<=mid) ans+=query(node[p].left,b,mid,l,std::min(mid,r));
			if(r>mid) ans+=query(node[p].right,mid+1,e,std::max(mid+1,l),r);
			return ans;
		}
};
SegmentTree t;
int gcd(const int &a,const int &b) {
	return b?gcd(b,a%b):a;
}
int main() {
	const int n=getint();
	for(register int i=2;i<=n;i++) left[i]=getint();
	min[par[n]=n]=left[n];
	for(register int i=n-1;i;i--) {
		min[i]=std::min(min[i+1],left[i]);
		par[i]=par[min[i]]?:i;
	}
	for(register int i=2;i<=n;i++) {
		if(par[i]==i) t.modify(t.root[i]=t.root[min[i]],1,n,1,i-1);
	}
	for(register int i=2;i<=n;i++) {
		t.root[i]=t.root[i]?:t.root[par[i]];
	}
	for(register int q=getint();q;q--) {
		const int l=getint(),r=getint(),x=getint();
		int ans=r-l+1;
		if(l<left[x]) ans+=t.query(t.root[left[x]],1,n,l,std::min(r,left[x]-1));
		const int d=gcd(ans,r-l+1);
		printf("%d/%d\n",ans/d,(r-l+1)/d);
	}
	return 0;
}
posted @ 2018-06-09 19:33  skylee03  阅读(846)  评论(0编辑  收藏  举报