NOIP 2012 Senior 3
来源:互联网 发布:生死狙击软件免费版 编辑:程序博客网 时间:2024/06/06 00:23
还是太弱了…这道题拿到手上后完全没有能够在规定时间内解决的思路。不过还好,大体思路是对的:
首先预处理A、B在每个地方开一次车到达的地方。对于第一问,枚举A出发的位置,对于第二问,直接计算就行了。计算的方法就是挨着推,直到满足题目中结束旅行的条件。很明显,预处理的时间复杂度为
这个题的思路就是这样,我们要做的就是将上面的两个操作至少优化到
由于旅行只能从左往右走,因此我们只能从右往左向set中添加结点。假设有5座城市1,2,3,4,5,我们要计算A、B各自从3出发到达的下一个城市:
3 / \4 5
这里假设4比3低,5比3高,那么树将会是以上这样。
我们可以使用set的find方法找到3的位置,然后对迭代器进行++,–,就能找到4,5了。
那么到底A该走哪里,B该走哪里呢?假设我们要计算1:
1 / \ 3 5 //这棵树是我乱编的 / \4 2
所以我们现在知道的是可能的答案为3,4,5,2,即向左扩展2个结点,向右扩展2个结点(想一想,为什么)。我们对这四个结点的进行排序,第0个就是B的目的地,第1个就是A的目的地。当然,如果A没有目的地,就不要改A。
对于计算这个操作,我们可以使用倍增的思想。之前我们一次走一步,时间复杂度为
next[k][i]
代表从i出发,AB开了2^k轮车后到达的位置(一轮指AB先后开了一次车)
disA[k][i]
代表从i出发,AB开了2^k轮车后A走的里程
disB[k][i]
代表从i出发,AB开了2^k轮车后B走的里程
如果遇到最后只有A开了车,需要单独计算
有没有感觉这和ST表的定义很像?事实上,ST表就是用的倍增的思想。
枚举时,我们将k从大到小进行枚举(想一想,为什么不能从小到大)。如果开2^k轮车后能走到一个地方,则开,否则k--
。k最终枚举到0,这毋庸置疑,那k从哪里开始枚举呢?我们可以从可能的极大值开始。因为如果k很大,肯定是开不到的,只是浪费一点时间而已。
最后,我们根据初始化的信息推算即可,详见代码。
参考代码
#include <cstdio>#include <cstdlib>#include <cmath>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <string>#include <stack>#include <queue>#include <deque>#include <map>#include <set>using std::cin;using std::cout;using std::endl;#define FOR(x, f, t) for(int x = (f); x <= (t); x++)inline int readIn(){ int a; scanf("%d",&a); return a;}const int maxn = 100005;const int maxm = 100005;int n,m;struct CITY{ int index; int height; CITY(int index = 0, int height = 0):index(index), height(height) { } bool operator< (const CITY& b) const { return height < b.height; }} cities[maxn];int start[maxm];int x[maxm];int height;struct sub //用于初始化{ CITY destination; void operator=(const CITY& b) { destination = b; } bool operator<(const sub& b) const { int s1 = std::abs(height - destination.height); int s2 = std::abs(height - b.destination.height); if(s1==s2) return destination.height < b.destination.height; return s1<s2; }} subs[4];int nextA[maxn];int nextB[maxn];const int maxIndex = 17;int next[maxIndex][maxn]; //从i开2^k轮后到的位置long long disA[maxIndex][maxn]; //从i开2^k轮A的路程long long disB[maxIndex][maxn]; //从i开2^k轮B的路程void input(){ n=readIn(); FOR(i, 1, n) { cities[i].height=readIn(); cities[i].index = i; } x[0]=readIn(); m=readIn(); FOR(i, 1, m) { start[i]=readIn(); x[i]=readIn(); }}void init(){ std::set<CITY> des; des.insert(cities[n]); for(int i = n-1; i >= 1; i--) //要从右往左更新,因为只能从左往右走 { des.insert(cities[i]); //先插入:我们要知道要更新的城市的位置 std::set<CITY>::iterator it = des.find(cities[i]); //要更新的城市的位置 int nCmp = 0; if(it!=des.begin()) { it--; subs[nCmp++] = *it; if(it!=des.begin()) { it--; subs[nCmp++] = *it; it++; } it++; } if(it!=des.end()) { it++; //不用复原了 subs[nCmp++] = *it; if(it!=des.end()) { it++; subs[nCmp++] = *it; } } height = cities[i].height; std::sort(subs, subs+nCmp); nextB[i] = subs[0].destination.index; if(i<=n-2) nextA[i] = subs[1].destination.index; }}void go(int from, int X, long long& lengthA, long long& lengthB){ for(int i = maxIndex - 1; ~i; i--) //相当于二进制 { if(next[i][from] && disA[i][from] + disB[i][from] <= X) { lengthA += disA[i][from]; lengthB += disB[i][from]; X -= disA[i][from] + disB[i][from]; from = next[i][from]; } } int last = nextA[from]; //看看A还能不能走 if(!last) return; int lastDis = std::abs(cities[last].height - cities[from].height); if(lastDis <= X) lengthA+=lastDis;}void run(){ input(); init(); //初始化A,B开一次开到哪里去了 for(int i = 1; i <= n; i++) //初始化开1轮的情况 { int pos1 = nextA[i]; int pos2 = nextB[pos1]; if(pos1) disA[0][i] = std::abs(cities[pos1].height - cities[i].height); if(pos2) disB[0][i] = std::abs(cities[pos2].height - cities[pos1].height); next[0][i] = pos2; } for(int i = 1; i <= maxIndex - 1; i++) //初始化开2^k轮的情况 { for(int j = 1; j <= n; j++) { next[i][j] = next[i - 1][next[i - 1][j]]; disA[i][j] = disA[i - 1][j] + disA[i - 1][next[i - 1][j]]; disB[i][j] = disB[i - 1][j] + disB[i - 1][next[i - 1][j]]; } } //solve1 long long ansA=1e15; long long ansB=0; int ans = 0; for(int i = 1; i <= n; i++) { long long lengthA=0, lengthB=0; go(i, x[0], lengthA, lengthB); if(lengthB && (!ans || ansA * lengthB > ansB * lengthA)) { ans = i; ansA = lengthA; ansB = lengthB; } } printf("%d\n",ans); //solve2 for(int i = 1; i <= m; i++) { long long lengthA=0, lengthB=0; go(start[i], x[i], lengthA, lengthB); cout<<lengthA<<" "<<lengthB<<endl; printf("%lld %lld\n",lengthA, lengthB); }}int main(){ run(); return 0;}
- NOIP 2012 Senior 3
- NOIP 2012 Senior 2
- NOIP 2012 Senior 5
- NOIP 2009 Senior 3
- NOIP 2011 Senior 3
- NOIP 2015 Senior 3
- NOIP 2014 Senior 3
- NOIP 2013 Senior 3
- NOIP 2016 Senior 3
- NOIP 2003 Senior 3
- NOIP 2005 Senior 3
- NOIP 2017 Senior 3
- NOIP 2009 Senior 1
- NOIP 2009 Senior 4
- NOIP 2011 Senior 2
- NOIP 2011 Senior 4
- NOIP 2011 Senior 5
- NOIP 2011 Senior 6
- OpenStack多节点安装(二):Keystone
- 子进程的异步等待方式
- linux配置远程debug
- sed(流编辑器)基础知识
- 无法解析或打开软件包的列表或是状态文件 解决方案
- NOIP 2012 Senior 3
- 475. Heaters
- CAN 显性和隐性
- Discuz!完美去除版权方法教程
- 暑假第一周学习总结
- 纪念工作一周年--开篇
- HDU5113 Black And White
- 创建FTP服务器
- OpenCV图像边缘检测(Laplace算法)