#6435. 「PKUSC2018」星际穿越

考场上写出了70分,现在填个坑

比较好写的70分是这样的:(我考场上写的贼复杂)

\(L(i)=\min_{j=i}^nl(j)\)

那么从i开始向左走第一步能到达的就是\([l(i),i-1]\)(显然)

第二步能到达的是\([L(l(i)),l(i)-1]\)

为什么呢,因为i一开始可以直接向左,也可以先向右走到\(l\)最小的位置然后向左,这时能跳到的区间就是\([L(i),i]\)

如果\(L(l(i))=L(i)\)显然这是一种方案,但是有可能\(L(l(i))<L(i)\),既然小了肯定是\(l(k)(k\in[l(i),i))\)这一部分\(<L(i)\),那么当然也是可行的

后面就是\([L(L(\cdots L(l(i))\cdots)),上一个的左端点]\)了,一直推下去。

#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int l[300010],L[300010];
ll D[5010][5010];
//D[i][j]表示从i开始到[j,i-1]的跳的次数之和
int main(){
	int n=gi();
	for(int i=2;i<=n;++i)l[i]=gi();
	L[n]=l[n];for(int i=n-1;i>1;--i)L[i]=std::min(l[i],L[i+1]);
	for(int i=2;i<=n;++i){
		int x=l[i],lst=i;
		for(int j=x;j<lst;++j)D[i][j]=1;
		int d=1;
		while(x!=1){
			lst=x;x=L[x];++d;
			for(int j=x;j<lst;++j)D[i][j]=d;
		}
		for(int j=1;j<i;++j)D[i][j]+=D[i][j-1];
	}
	int q=gi(),l,r,x;
	while(q--){
		l=gi(),r=gi(),x=gi();
		ll A=D[x][r]-D[x][l-1],B=r-l+1,g=std::__gcd(A,B);
		printf("%lld/%lld\n",A/g,B/g);
	}
	return 0;
}

优化很显然是倍增,很容易想到\(f[i][j]\)设为\(i\)位置向前跳\(L(i)\)\(2^j\)次。

再来一个\(g[i][j]\)设为\(i\)位置向前跳\(2^j\)次,这中间每个位置跳的次数的和。随便理解一下就行了。



#include<bits/stdc++.h>
#define il inline
#define vd void
typedef long long ll;
il int gi(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return x*f;
}
int l[300010],L[300010];
int f[19][300010];
ll g[19][300010];
il ll solve(int x,int y){
	if(l[x]<=y)return x-y;
	ll ret=x-l[x];int i,j;
	x=l[x];
	for(i=18,j=1;~i;--i)
		if(f[i][x]>=y){
			ret+=g[i][x]+j*(x-f[i][x]);
			j+=1<<i;
			x=f[i][x];
		}
	return ret+(x-y)*(j+1);
}
int main(){
	int n=gi();
	for(int i=2;i<=n;++i)l[i]=gi();
	L[n]=l[n];for(int i=n-1;i>1;--i)L[i]=std::min(l[i],L[i+1]);
	for(int i=1;i<=n;++i)f[0][i]=L[i],g[0][i]=i-L[i];
	for(int i=1;i<19;++i)
		for(int j=1;j<=n;++j){
			f[i][j]=f[i-1][f[i-1][j]];
			g[i][j]=g[i-1][j]+g[i-1][f[i-1][j]]+(1<<i-1)*(f[i-1][j]-f[i][j]);
		}
	int q=gi(),l,r,x;
	while(q--){
		l=gi(),r=gi(),x=gi();
		ll A=solve(x,l)-solve(x,r+1),B=r-l+1,g=std::__gcd(A,B);
		printf("%lld/%lld\n",A/g,B/g);
	}
	return 0;
}
posted @ 2018-11-26 20:24  菜狗xzz  阅读(383)  评论(0编辑  收藏  举报