開車旅行(NOIP2012提高組)

題目鏈接
這道題最基本的思路是用倍增,但是其實它的難點在預處理部分。
倍增的部分此次就不細說了,和之前的最近公共祖先的思想類似。
我們主要來探討一下預處理的部分。
我們需要預處理出每個城市小A和小B的選擇目標和對應的距離,接下來就可以處理出進行2k輪開車的目的地和距離了。所以前者纔是重中之重,而前者如果要用暴力的方法會tle的。
有人可能會疑惑,我們找當前點的後面兩三個不就可以了?爲什麼會tle呢?
實際上並不是序號相差很遠距離就很遠,實際上有可能第一個城市和最後一個城市最近,可以舉個例子,城市海拔如下:
1 3 4 5 6 7 8 9 10 11 2
也許你也會有疑問,不是說右邊的城市在左邊城市的東邊嗎?這可能嗎?
注意這題並不是線性的,而是2D的。所以可以畫出如下的圖:
在這裏插入圖片描述

所以,這麼看來,暴力算法的複雜度就是n方,就會超時。
所以我們需要換種方法。
我們不妨換種順序,因爲是用海拔來決定高度,所以海拔相距最近的,一定是距離最近的。
所以我們可以按照海拔進行排序,排好序後就按照這個順序建立雙向鏈表。
這時我們就知道了,對於某一城市,小a和小b要麼沒有選擇,要麼選擇的城市一定在當前城市鏈表中的前兩個和兩個中。
爲什麼不是前一個和後一個?因爲如果一個點在鏈表邊上,它就只有一側,所以把特殊情況考慮在內,就是前兩個和後兩個。
那麼你可能又會覺得奇怪,萬一這幾個當中有城市在當前城市的西邊怎麼辦?這樣這個範圍就可能不夠了?
所以我們的處理順序就成了很大的很關鍵,我們按照城市編號順序去處理每個城市的目標,處理完後就把當前城市刪除(這也是用雙向鏈表的原因),這樣每次處理就可以保證當前鏈表中的其他城市均在當前城市的東邊,而保證我們所取的範圍是夠的。
當然你也許還會有疑問,那我怎麼定位要處理的城市在鏈表中的位置呢?
這個準備的指針數組就好了。
這樣就可以把預處理的複雜度從O(n2)降低到O(n),這樣就可以過了
下面是參考代碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAX 2000000000
using namespace std;
const int maxn=100005;
struct City{
	int num;
	int h;
	City *last,*next; 
}city[maxn]; 
City *pos[maxn];
long long n,m,x0,ss[maxn],xx[maxn],h[maxn],maxk; 
long long aaim[maxn],baim[maxn],adis[maxn],bdis[maxn];
long long longdis[maxn][20],longaim[maxn][20];
long long alongdis[maxn][20],alongaim[maxn][20];
long long s0,da=-1,db=-1;
void fun1(long long curs){
	long long tot=0,cura=0,curb=0,curn=curs;
	for(int k=maxk;k>=0;k--){
		if(longaim[curn][k]!=0&&longdis[curn][k]+tot>x0){
			continue;
		}
		if(longaim[curn][k]==0){
			continue;
		}
		tot+=longdis[curn][k];
		cura+=alongdis[curn][k];
		curn=longaim[curn][k];
	}
	curb=tot-cura;
	if(aaim[curn]!=0&&tot+adis[curn]<=x0){
		tot+=adis[curn];
		cura+=adis[curn];
	}
	if(da==-1){
		s0=curs;
		da=cura;
		db=curb;
	}else if(db==0&&curb==0&&h[curs]>h[s0]){
		s0=curs;
		da=cura;
		db=curb;
	}else if(db==0){
		s0=curs;
		da=cura;
		db=curb;
	}else if(curb==0){
		
	}else if(da*curb>db*cura){
		s0=curs;
		da=cura;
		db=curb;
	}else if(da*curb==db*cura&&h[curs]>h[s0]){
		s0=curs;
		da=cura;
		db=curb;
	} 
}
void fun2(long long s,long long xi){
	long long tot=0,cura=0,curb=0,curn=s;
	for(int k=maxk;k>=0;k--){
		if(longaim[curn][k]!=0&&longdis[curn][k]+tot>xi){
			continue;
		}
		if(longaim[curn][k]==0){
			continue;
		}
		tot+=longdis[curn][k];
		cura+=alongdis[curn][k];
		curn=longaim[curn][k];
	}
	curb=tot-cura;
	if(aaim[curn]!=0&&tot+adis[curn]<=xi){
		tot+=adis[curn];
		cura+=adis[curn];
	}
	printf("%d %d\n",cura,curb); 
}
int cmp(City a,City b){
	return a.h<b.h;
}
void update(City cur,City aim){
	if((abs(cur.h-aim.h)<bdis[cur.num])||(abs(cur.h-aim.h)==bdis[cur.num]&&aim.h<pos[baim[cur.num]]->h)){
		adis[cur.num]=bdis[cur.num];
		aaim[cur.num]=baim[cur.num];
		bdis[cur.num]=abs(cur.h-aim.h);
		baim[cur.num]=aim.num;
	}else if((abs(cur.h-aim.h)<adis[cur.num])||(abs(cur.h-aim.h)==adis[cur.num]&&aim.h<pos[aaim[cur.num]]->h)){
		adis[cur.num]=abs(cur.h-aim.h);
		aaim[cur.num]=aim.num;
	}
}
int main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&city[i].h);
		adis[i]=bdis[i]=MAX;
		city[i].num=i;
	}
	scanf("%lld%lld",&x0,&m);
	for(int i=0;i<m;i++){
		scanf("%lld%lld",&ss[i],&xx[i]);
	}
	sort(city+1,city+n+1,cmp);

	for(int i=1;i<=n;i++){
		if(i==1){
			city[i].last=NULL;
		}else{
			city[i].last=&city[i-1];
		}
		if(i==n){
			city[i].next=NULL;
		}else{
			city[i].next=&city[i+1];
		}
		pos[city[i].num]=&city[i];
	}

	for(int i=1;i<=n;i++){
		City *p;
		p=pos[i]->last;
		if(p!=NULL){
			update(*pos[i],*p);
			p=p->last;
			if(p!=NULL){
				update(*pos[i],*p);
			}
		}
		p=pos[i]->next;
		if(p!=NULL){
			update(*pos[i],*p);
			p=p->next;
			if(p!=NULL){
				update(*pos[i],*p);
			}
		}
		if(pos[i]->next!=NULL){
			pos[i]->next->last=pos[i]->last;
		}
		if(pos[i]->last!=NULL){
			pos[i]->last->next=pos[i]->next;
		}
		 
	} 
	for(int i=1;i<=n;i++){
		if(aaim[i]!=0&&baim[aaim[i]]!=0){
			longdis[i][0]=adis[i]+bdis[aaim[i]];
			longaim[i][0]=baim[aaim[i]];
			alongdis[i][0]=adis[i];
			alongaim[i][0]=baim[aaim[i]];
		}else{
			longaim[i][0]=0;
			longdis[i][0]=MAX;
			alongdis[i][0]=MAX;
			alongaim[i][0]=0;
		}
	}
	for(int k=1;(1<<k)<=n;k++){
		for(int i=1;i<=n;i++){
			if(longaim[i][k-1]!=0&&longaim[longaim[i][k-1]][k-1]!=0){
				longdis[i][k]=longdis[i][k-1]+longdis[longaim[i][k-1]][k-1];
				longaim[i][k]=longaim[longaim[i][k-1]][k-1];
			}else{
				longdis[i][k]=MAX;
				longaim[i][k]=0;
			}
			if(alongaim[i][k-1]!=0&&alongaim[alongaim[i][k-1]][k-1]!=0){
				alongdis[i][k]=alongdis[i][k-1]+alongdis[alongaim[i][k-1]][k-1];
				alongaim[i][k]=alongaim[alongaim[i][k-1]][k-1];
			}else{
				alongdis[i][k]=MAX;
				alongaim[i][k]=0;
			}
			
		}
		maxk=k;
	}	
	for(int i=1;i<=n;i++){
		fun1(i);
	}
	printf("%d\n",s0);
	for(int i=0;i<m;i++){
		fun2(ss[i],xx[i]);
	}
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章