zoukankan      html  css  js  c++  java
  • [PKUSC2018]星际穿越

    [PKUSC2018]星际穿越

    题目大意:

    有一排编号为(1sim n)(n(nle3 imes10^5))个点,第(i(ige 2))个点与([l_i,i-1])之间所有点有双向边。(q(qle3 imes10^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(nlog 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
    ",ans/d,(r-l+1)/d);
    	}
    	return 0;
    }
    
  • 相关阅读:
    ES6 变量的解构赋值
    【js重学系列】new
    【js面试系列】手写常见js方法
    【js重学系列】this
    js-继承
    【js重学系列】数组高阶函数
    【js面试系列】数组去重
    云服务器部署项目-基本使用流程
    mongodb-基本使用
    移动端适配
  • 原文地址:https://www.cnblogs.com/skylee03/p/9160594.html
Copyright © 2011-2022 走看看