自己的AC做法似乎离正解偏了十万八千里而且复杂了不少……不管怎样还是记录下来吧。

题意:

题目链接:
JOISC2017 F - AtCoder
JOISC2017 F - LOJ

\(N\)个车站排成一行,第\(i\)个车站有一个在\([1,M]\)之间的权值\(l_i\)。有\(M\)种列车,第\(i\)种在所有满足\(l_j \geq i\)的车站\(j\)停靠。如果搭乘一种列车从\(u\)到\(v\),需要在\(u\)、\(v\)之间的所有停靠站(包含\(u\)和\(v\)),各停车一次,定义这段行程的代价为(\(停车次数-1\))。如果列车\(i\)和\(j\)都在车站\(k\)停靠,允许在车站\(k\)从\(i\)换乘到\(j\)。列车行驶的方向任意。\(Q\)次询问,每次给出起点\(s\)和终点\(t\),问从\(s\)坐车到\(t\)的最小总代价。

\(1 \leq N,M,Q \leq 10^5\)

思路:

首先试着构出一张无向图,在可以用代价\(1\)转移的点之间连边。

构图方案:

1、标记所有\(l_i=M\)的车站\(i\),在相邻(中间没有被标记车站)的被标记车站之间连边。
2、新标记所有\(l_i=M-1\)的车站\(i\)(保留先前标记),相邻的被标记车站连边。
3、新标记所有\(l_i=M-2\)的车站\(i\)(保留先前标记),相邻的被标记车站连边。
……

注意到我们标出的这些边形成了一张分层图,其中第\(i\)层的边集就是在以上构图方法中,第\(M+1-i\)步添加的边。不妨称序号大的层级为高层。

如果把第\(i\)层从点\(j\)到点\(k\)的边,画成平面上一条从\((j,i)\)到\((k,i)\)的线段,那么对第一个样例画出的图是:

注意到性质:高层线段的端点集合,一定是低层线段端点集合的子集;高层的线段,一定完整包含了低层对应位置的线段,且线段之间要么包含要么相离。

此外易见,对每一个\(i\),存在点\(i\)作为端点的最高层级是\(l_i\)。

于是问题转化成,在这张分层图上,要从\((s,1)\)走到\((t,1)\),允许以下两种转移,问最小代价:

1、如果点\(a\)是层级\(b\)和\(b+1\)的端点,则以代价\(0\)在\((a,b)\)与\((a,b+1)\)之间双向转移。
2、如果点\(a\)不是层级\(b\)中最靠左的端点,且在层级\(b\)中,点\(a\)左侧的相邻端点是\(c\),则以代价\(1\)在\((a,b)\)与\((c,b)\)之间双向转移。

接下来就是喜闻乐见的推结论时间了!

结论1:一定有一条最优路径,满足其经过的层级高度是按访问顺序先增后减的。

证明:

考虑对于起终点相同,横坐标都单调递增(减)的两条路线\(A\)和\(B\),如果\(A\)的高度始终不低于\(B\),那么\(A\)经过的边数一定不大于\(B\)。

理由是:“高层的线段,一定完整包含了低层对应位置的线段”,所以走高层一定不会更差。

接下来考虑反证,假设这个路径的某一段形如下图的黑色路线:

那么由先前结论,一定可以在达到最高点后改走红色路线,使答案不变得更差,同时保持凸性。

对于黑色路线横坐标不单调的情况,用类似方法可以证明同样的结论,有些繁琐,在此略去。

结论2:记\(L(a,b)\)为层级\(b\)中,点\(a\)左侧最近的端点(当\(a\)是端点时\(L(a,b)\)=\(a\));\(R(a,b)\)为层级\(b\)中,点\(a\)右侧最近的端点。那么,从\((a,1)\)到\(L(a,b)\)(或者\(R(a,b)\))的最短路径,在第\(i\)层首次经过的点一定是\(L(a,i)\)或\(R(a,i)\)。

这个结论之后再证。

结论3:对\(s<t\),从\((s,1)\)到\((t,1)\)的最短路径所经过的最大层级,要么是数组\(l\)在下标区间\([s,t]\)上的最大值\(Max\),要么是\(Max+1\)。

对于结论2、3的证明:

考虑在下图中,当前位于点\(A\),路径的起终点是第一层的\(s\)和\(t\),且现在处于路径的上升阶段。

图中\(B=L(A,i+1)\),\(C=R(A,i+1)\)。那么图中从\(A\)到\(t\)有以下几种可能的路径:

1、黑色路径:在第\(i\)层向右走直到横坐标超过\(C\)的横坐标,再沿着最优路径走到\(t\)。
2、褐色路径:在第\(i\)层向右走,再向上走到\(C\),再沿着最优路径走到\(t\)。
3、深蓝+褐色路径:沿着深蓝色路径经过\(B\)走到\(C\),再沿着褐色路径的后一半走到\(t\)。

对于起终点相同,横坐标都单调递增(减)的两条路线\(A\)和\(B\),如果\(A\)的高度始终不低于\(B\),那么\(A\)经过的边数一定不大于\(B\)。

所以,路径2一定不长于路径1。
换句话说,只要存在点\(C\)在\(i+1\)层,且\(C\)的横坐标在\(s\)和\(t\)之间,走到\(i+1\)层一定比留在第\(i\)层更优。
再换句话说,最短路径经过的最大层级,不小于\(Max\)。结论3的后一半之后再证。

接着考虑用数学归纳证明结论2。

归纳假设:最优路径在第\(i\)层首次经过的点一定是\(L(a,i)\)或\(R(a,i)\)。

当\(i=1\)时:易证。

当\(i=k\)时:
图中的\(A\)为\(L(a,k)\)或\(R(a,k)\),则易知\(B=L(a,k+1)\),\(C=R(a,k+1)\)。
对于在\(C\)右侧的\(C'\),在第\(i+1\)层首次经过的点是\(C'\)的路径,一定是路径1,不是最优路径;
对于在\(B\)左侧的\(B'\),如果一条路径在\(i+1\)首先经过的点是\(B'\):
1°:如果到达\(B'\)后向右行走,则这条路径一定不优于图中的路径3(起终点相同,代价却更大);
2°:如果到达\(B'\)后向左/上行走,易证,从\((B,k+2)\)向左走,一定能走到同样的第\(k+2\)层的结点,且代价不增加。
综上,在第\(k+1\)层经过的第一个点,必为\(B\)或\(C\)。

所以,如果\(i=k\)时命题成立,则对于\(i=k+1\)也成立。

结论2证毕。

由结论2可知,在最高层\(h\),如果\(h>Max\),在这一层一定只会走一步:从\(L(s,h)\)(或\(R(s,h)\))到\(L(t,h)\)(或\(R(t,h)\)),代价恒为1。

又因为\(L(s,Max+2) \leq L(s,Max+1) , R(s,Max+2) \geq R(s,Max+1)\)(对\(t\)同理),所以\(h=Max+2\)时的代价不小于\(h=Max+1\)时的代价。

同理易证第\(Max+1\)层不比任何更高层更差。

所以最高层数不小于\(Max\),不大于\(Max+1\),结论3证毕。

呼……推完结论,终于开始设计算法了。

首先,回答查询的时候,显然应该枚举最高层级\(h\)(\(Max\)到\(Max+1\)),再把以下三个数相加:
1、\(s\)走到这一层的最小代价;
2、\(t\)走到这一层的最小代价;
3、\(s\)、\(t\)在这一层会合的最小代价;
(当然,\(s\)、\(t\)在最高层的首次访问位置是\(L(...,h)\)还是\(R(...,h)\),还要对这个分四类讨论)
第三项很好求,在此只讲前两项的求法。

这种不断向一个方向跳的问题,不是一看就是倍增吗

考虑倍增,\(dp[i][j]\)表示从\((j,1)\)到\(L(j,2^i)\)和\(R(j,2^i)\)的距离?
等等,不对啊……这玩意怎么转移啊……
倍增算法可行的前提是,如果你把跳\(2^i\)步拆成跳两次\(2^{i-1}\)步,则前一步跳到的中继点,必须有一个状态来代表。

试了好几种比较裸的倍增方法,都不可行……看来得再分析一波了。

等等,其实我们关心的是\((i,1)\)与\(L(i,j)\)、\(R(i,j)\)之间的距离,而我们可以用二元组\(\{L(i,j),R(i,j)\}\)来代替状态中的\(i\)?

具体地说,这里,二元组\(\{L,R\}\)表示“当前所在位置的行(层级)不确定,(在结论2中的)列序号可以是\(L\)也可以是\(R\)”。

hint:注意每一个二元组都只在一个层级区间存在,即对于确定的\(\{L(i,j),R(i,j)\}\),\(j\)的可能值是一个区间。

而且发现这种二元组只有\(O(N)\)个?
(证明方法,二元组数量就是构出的图中的不同的边数,而构图时每标记一个新点,最多产生两条新边。)

而且在不断往上跳的过程中,每一个二元组\(P\)的后继是唯一的?(后继是说,从\(P\)往上跳的过程中,达到的第一个与自己不相等的二元组)

而且只有在从二元组\(P\)跑到\(P\)的后继时才会产生代价?(也就是说同一个二元组代表的所有状态代价一样)

于是想到:\(dp[i][P]\)表示,从二元组\(P\)开始往上跳,所在的二元组改变\(2^i\)次(即当前在\(P\)的第\(2^i\)个后继)后的最小代价。

hint:注意\(dp[i][P]\)是两个值,分别表示到达二元组的左侧、右侧结点的代价。

前一步跳到的中继点,必须有一个状态来代表。

满足了吗?满足了。

啥?你说\(N^2 \log M\)大小的数组开不下?那是你的问题咯……好好想想怎么高效的把二元组放进状态里。

查询的时候沿着倍增指针不断地跳,直到当前二元组的存在区间囊括了目标层级为止。

总时间复杂度\(O((N+Q) \log M)\)。

接下来就是愉快的代码时间啦!

代码:

#include <bits/stdc++.h>
using namespace std;
#define iinf 2000000000
#define linf 1000000000000000000LL
#define ulinf 10000000000000000000ull
#define MOD1 1000000007LL
#define mpr make_pair
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned long UL;
typedef unsigned short US;
typedef pair < int , int > pii;
clock_t __stt;
inline void TStart(){__stt=clock();}
inline void TReport(){printf("\nTaken Time : %.3lf sec\n",(double)(clock()-__stt)/CLOCKS_PER_SEC);}
template < typename T > T MIN(T a,T b){return a<b?a:b;}
template < typename T > T MAX(T a,T b){return a>b?a:b;}
template < typename T > T ABS(T a){return a>0?a:(-a);}
template < typename T > void UMIN(T &a,T b){if(b<a) a=b;}
template < typename T > void UMAX(T &a,T b){if(b>a) a=b;}
const int lg=17,inf=1000000;
vector < int > prt[100005],lev[100005];
int n,m,q,l[100005],mx[lg][100005],dis[2][100005],nx[100005];
struct data{int ld,rd,lc,rc;void set(int LD,int RD,int LC,int RC){ld=LD;rd=RD;lc=LC;rc=RC;if(!ld) lc=inf;}void tran(data L,data R){if(L.ld==-1) L.lc=L.rc=inf;if(R.ld==-1) R.lc=R.rc=inf;int nlc=MIN(lc+L.lc,rc+R.lc),nrc=MIN(lc+L.rc,rc+R.rc);set((L.ld==-1?R.ld:L.ld),(L.ld==-1?R.rd:L.rd),nlc,nrc);}
};
vector < data > dp[lg][100005][2];
int prev(int i,int l){// before or equalint j;for(j=lg-1;j>=0;--j){if(i-(1<<j)>=-1 && mx[j][i-(1<<j)+1]<l){i-=(1<<j);}}return i;
}
int nxt(int i,int l){// afterint j;++i;for(j=lg-1;j>=0;--j){if(i+(1<<j)<=n && mx[j][i]<l){i+=(1<<j);}}return i;
}
int qrymax(int l,int r){int le;for(le=0;(1<<(le+1))<r-l+1;++le);return MAX(mx[le][l],mx[le][r-(1<<le)+1]);
}
int qrydis(int s,int t,int le){int cnt=0;if(s>t) swap(s,t);while(s<t){if(le==l[s]){cnt+=dis[1][s]%inf;s=nxt(s,le+1);}else{++cnt;s=nxt(s,le);}}if(t<s && le==l[t]){cnt-=dis[1][t]%inf;}return cnt;
}
data qrydisV(int s,int le){data cur,nxt;if(s==n-1)cur.set(s-1,0,1,0);elsecur.set(s,0,0,1);int i;for(i=lg-1;i>=0 && cur.ld!=-1;--i){nxt=cur;nxt.tran(dp[i][nxt.ld][0][nxt.rd],dp[i][nxt.ld][1][nxt.rd]);if(nxt.ld!=-1 && (nxt.rd?lev[nxt.ld][nxt.rd-1]:-1)<le)cur=nxt;if(cur.ld!=-1 && lev[cur.ld][cur.rd]>=le) break;}if(cur.ld!=-1 && lev[cur.ld][cur.rd]<le) cur.ld=-1;return cur;
}
int main(){// inputting start// 数据结构记得初始化! n,m别写反!int i,j,k,fsv=-1,fsp=-1,scv=-1;scanf("%d%d%d",&n,&m,&q);for(i=1;i<=n;++i){scanf("%d",l+i);--l[i];if(l[i]>fsv){fsv=l[i];fsp=i;}else if(l[i]>scv){scv=l[i];}}l[fsp]=scv;l[0]=m++;++n;#ifdef LOCALTStart();#endif// calculation start// 数据结构记得初始化! n,m别写反!for(i=0;i<n;++i) mx[0][i]=l[i];for(i=1;i<lg;++i){for(j=0;j<n;++j){int nl=j+(1<<(i-1));if(nl<n)mx[i][j]=MAX(mx[i-1][j],mx[i-1][nl]);elsemx[i][j]=mx[i-1][j];}}for(i=n-1;i>=0;--i){nx[i]=nxt(i,l[i]+1);int suc=nxt(i,l[i]);if(suc>=n)dis[1][i]=inf;else if(l[suc]>l[i])dis[1][i]=1;elsedis[1][i]=dis[1][suc]+1;for(j=i+1;j<suc;j=nx[j]){prt[i].push_back(j);lev[i].push_back(l[j]);}if(suc<n){prt[i].push_back(suc);lev[i].push_back(l[i]);}dp[0][i][0].resize(prt[i].size());dp[0][i][1].resize(prt[i].size());for(j=0;j<(int)prt[i].size()-1;++j){dp[0][i][0][j].set(i,j+1,0,1);dp[0][i][1][j].set(i,j+1,1,MIN(2,dis[1][prt[i][j]]));}}for(i=0;i<n;++i){int prd=prev(i,l[i]+1),prv=prev(i-1,l[i]),d=(int)prt[i].size()-1;if(prv==-1)dis[0][i]=inf;else if(l[prv]>l[i])dis[0][i]=1;elsedis[0][i]=dis[0][prv]+1;if(d==-1) continue;if(prd!=-1 && lev[prd].back()<=l[i]) prd=prev(prd-1,l[i]+1);if(prd==-1 || lev[prd].empty() || lev[prd].back()<=l[i])dp[0][i][0][d].ld=dp[0][i][1][d].ld=-1;else{int nd=lower_bound(lev[prd].begin(),lev[prd].end(),l[i]+1)-lev[prd].begin();int disL=dis[0][i],disR=dis[1][prt[i][d]];if(prt[i][d]==prt[prd][nd]) disR=0;dp[0][i][0][d].set(prd,nd,MIN(disL,disR+2),MIN(disL+1,disR+1));dp[0][i][1][d].set(prd,nd,MIN(disR+1,disL+1),MIN(disR,disL+2));}}for(i=1;i<lg;++i){for(j=0;j<n;++j){dp[i][j][0].resize(prt[j].size());dp[i][j][1].resize(prt[j].size());for(k=0;k<(int)prt[j].size();++k){int d;for(d=0;d<2;++d){data &trs=dp[i-1][j][d][k],&cur=dp[i][j][d][k];if(trs.ld==-1)cur.ld=-1;else{cur=trs;cur.tran(dp[i-1][cur.ld][0][cur.rd],dp[i-1][cur.ld][1][cur.rd]);}}}}}while(q--){int s[2];scanf("%d%d",s,s+1);sort(s,s+2);int mxl=qrymax(s[0],s[1]),res=inf;for(i=mxl;i<=mxl+1;++i){data tr[2];for(j=0;j<2;++j){tr[j]=qrydisV(s[j],i);if(tr[j].ld==-1) break;}if(j<2) break;int p[2],d[2];for(j=0;j<2;++j){p[0]=(j?prt[tr[0].ld][tr[0].rd]:tr[0].ld);d[0]=(j?tr[0].rc:tr[0].lc);for(k=0;k<2;++k){p[1]=(k?prt[tr[1].ld][tr[1].rd]:tr[1].ld);d[1]=(k?tr[1].rc:tr[1].lc);UMIN(res,d[0]+d[1]+qrydis(p[0],p[1],i));}}}printf("%d\n",res-1);}#ifdef LOCALTReport();#endifreturn 0;
}

转载于:https://www.cnblogs.com/turboboost/p/RailwayTrip.html

JOI2017 春季合宿:Railway Trip相关推荐

  1. 【JOI2017春季合宿】Port Facility

    http://uoj.ac/problem/356 题解 思路和\(NOIP\)双栈排序差不多. 对于两个元素,若\(l_1<l_2<r_1<r_2\)那么它们不能在一个栈里,我们连 ...

  2. JOI-2016/17 春季合宿 切题记

    17年的合宿好难啊...感觉是我做过的最难的一套题(没有之一)了...但是可能也是价值最高的? Day1: T1 Cultivation:给你一个H*W的网格,有N<=300棵仙人掌.每一年可以 ...

  3. BZOJ 4388 [JOI2012春季合宿]Invitation (线段树、二叉堆、最小生成树)

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=4388 题解 模拟Prim算法? 原题所述的过程就是Prim算法求最大生成树的过程.于是我 ...

  4. BZOJ 4221 [JOI2012春季合宿]Kangaroo (DP)

    题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=4221 题解 orz WYC 爆切神仙DP 首先将所有袋鼠按大小排序.考虑从前往后DP, ...

  5. LOJ #2731 [JOI2016春季合宿]Solitaire (DP、组合计数)

    题目链接 https://loj.ac/problem/2731 题解 首先一个很自然的思路是,设\(dp[i][j]\)表示选了前\(i\)列,第\(2\)行第\(i\)列的格子是第\(j\)个被填 ...

  6. LOJ #2733 [JOI2016春季合宿]Sandwiches (DP)

    题目链接 https://loj.ac/problem/2733 题解 神仙题-- 首先可以观察到一个结论: 目标块的两块小三明治一定分别是最后和倒数第二个被吃的. 由此我们可以考虑这两块谁先被吃.这 ...

  7. LOJ #2734 Luogu P3615 [JOI2016春季合宿]Toilets (结论、贪心)

    题目链接 (loj) https://loj.ac/problem/2734 (luogu) https://www.luogu.org/problem/P3615 题解 嗯,考场上肝了\(3h\)然 ...

  8. [JOI2012春季合宿]Rotate (链表)

    题意 题解 又是一道神仙题-- 显然的做法是大力splay,时间复杂度\(O((N+Q)N\log N)\), 可以卡掉. 正解: 使用十字链表维护矩阵,在周围增加第\(0\)行/列和第\((n+1) ...

  9. [JOI2012春季合宿]Constellation (凸包)

    题意 题解 神仙结论题. 结论: 一个点集合法当且仅当其凸包上的两种颜色点分别连续. 证明: 必要性显然. 充分性: 考虑对于一个不同色三角形\(ABC\),不妨设点\(A\)为白点,点\(B,C\) ...

最新文章

  1. springcloud 组件_深入理解 Spring Cloud 核心组件与底层原理
  2. python enumerate()函数
  3. python的类和实例化对象
  4. 关于类DOTA游戏多样化技能系统的设计思考
  5. 如何在Windows11和Windows10上获取驱动程序更新
  6. Python traceback模块的使用(抛异常、报错、输出错误)
  7. MapReduce:Job性能调优总结
  8. “发明在商业上获得成功”对专利法22条第三款有关创造性规定的影响
  9. 用‘栈’的思想编写一个十进制转换二进制、八进制或十六进制的程序
  10. openstack 排错
  11. 1_python基础—变量
  12. 学python要有多少英语词汇量测试_非常适合新手的一个Python爬虫项目: 打造一个英文词汇量测试脚本!...
  13. 腾讯百度等22家股东退出 链家注册资本缩水34%
  14. svn java.mine_最好的一般SVN忽略模式?
  15. MySQL(八)事务的隔离级别
  16. 计算机网络之万维网WWW
  17. Spring Boot 大文件上传(断点上传)、服务端分片下载、客户端分片下载(断点下载)
  18. word中图片为嵌入式格式时显示不全_word嵌入图片显示不全-不同类型文档中图片设置的几个小技巧...
  19. Keras中Conv1D和Conv2D的区别
  20. 2022 年 Java 行业分析报告

热门文章

  1. MySQL GROUP_CONCAT()函数的排序方法
  2. Zircon - Fuchsia 内核分析 - 启动(平台初始化)
  3. Fuchsia - 简析
  4. Solr参数mm详解
  5. python PyQt5程序运行界面无响应
  6. 已知随机变量X的协方差矩阵求去X的特征值 特征向量 PCA投影矩阵
  7. (31)ObjectARX2015 + vs2012选择集
  8. 魅族 刷机android 6.0,乐视X900+安卓6.0 魅族Flyme6刷机包 最新6.7.12.29R付费纯净版
  9. SmartGit功能介绍
  10. Gazebo模型制作dae