NOIP 2012 Day1 T3

这道题NOIP之前没码完。。身败名裂。。

很明显的树上倍增啦。。但有一个问题是如何快速获得每个点的两个最近和次远。。

用set。。

完了。。

细节见代码。。

// NOIP2012 Day1 T3

#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

 typedef long long LL;
 const int N=100000+5, LOG=21, INF=0x3f3f3f3f;

 #define rep(i,a,b) for (int i=a; i<=b; i++)
 #define dep(i,a,b) for (int i=a; i>=b; i--)
 #define read(x) scanf("%d", &x)
 #define fill(a,x) memset(a, x, sizeof(a))

 struct Node {
 	int h, id;
 	bool operator < (const Node x) const { return h<x.h; }
 }c[N];


 set<Node> S;
 int n, m, s, x, x0, ans, nextt[N][2], dist[N][2], g[N][LOG];
 LL f[N][LOG][2], a, b;

 void update(Node x, Node y) {  // 更新树边,0为最近点,1为次近点
 	if (!nextt[x.id][0]) {
 		nextt[x.id][0]=y.id;
 		dist[x.id][0]=abs(x.h-y.h);
 	}
 	else if (abs(x.h-y.h)<dist[x.id][0] || (abs(x.h-y.h)==dist[x.id][0] && y.h<c[nextt[x.id][0]].h)) {
 		nextt[x.id][1]=nextt[x.id][0];
		dist[x.id][1]=dist[x.id][0];
		nextt[x.id][0]=y.id;
		dist[x.id][0]=abs(x.h-y.h);
 	}
 	else if (!nextt[x.id][1] || abs(x.h-y.h)<dist[x.id][1] || (abs(x.h-y.h)==dist[x.id][1] && y.h<c[nextt[x.id][1]].h)) {
 		nextt[x.id][1]=y.id;
 		dist[x.id][1]=abs(x.h-y.h);
 	}
 }
 /*
  令
  g[i][j]为从i点走2^j个轮回后的位置,
  f[i][j][0]为从i点走2^j个轮回后A走过的距离,
  f[i][j][1]为从i点走2^j个轮回后B走过的距离。
 */
 void init_tree() {
 	rep(i,1,n) {
 		g[i][0]=nextt[nextt[i][1]][0];
 		f[i][0][0]=dist[i][1];
 		f[i][0][1]=dist[nextt[i][1]][0];
 	}
 	rep(j,1,20) 
 	  rep(i,1,n) {
 	  	g[i][j]=g[g[i][j-1]][j-1];
 	  	f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
 	  	f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
 	  }
 }

 void query(int s, int x, LL &da, LL &db) {  
    // 从城市s出发,最多走x距离,A、B分别开车的距离da、db
 	dep(i,20,0) if (f[s][i][0]+f[s][i][1]<=x && g[s][i]) {
 		da+=f[s][i][0];
 		db+=f[s][i][1];
 		x-=(f[s][i][0]+f[s][i][1]);
 		s=g[s][i];
 	}
 	if (nextt[s][1] && dist[s][1]<=x) da+=dist[s][1]; 
 	// 注意A可能还可以开一次
 }

int main()
{
	read(n);
	rep(i,1,n) {
		read(c[i].h);
		c[i].id=i;
	}
	set<Node>::iterator it;
	dep(i,n,1) {  //  按编号从大到小顺序建树
		S.insert(c[i]);
		it=S.find(c[i]); 
		// 找到c[i]的位置,并取出离它最近、次近的城市
		if (it!=S.begin()) {
			it--;
			update(c[i], *it);
			if (it!=S.begin()) {
				it--;
				update(c[i], *it);
				it++;
			}
			it++;
		}
		if ((++it)!=S.end()) {
			update(c[i], *it);
			if ((++it)!=S.end()) update(c[i], *it);
			it--;
		}
		it--;
	}
	init_tree();

	read(x0);
	a=1e+15, b=0;
	rep(i,1,n) {  // 对于第一问,枚举答案
		LL da=0, db=0;
		query(i, x0, da, db);
		if (db && (ans==0 || a*db>b*da)) ans=i, a=da, b=db;
	}
	printf("%d
", ans);

	read(m);
	rep(i,1,m) {
		read(s); read(x);
		LL da=0, db=0;
		query(s, x, da, db);
		printf("%I64d %I64d
", da, db);
	}
	
	return 0;
}



原文地址:https://www.cnblogs.com/yearwhk/p/5119875.html