[关闭]
@ysner 2018-07-20T19:02:27.000000Z 字数 2093 阅读 1911

[NOI2018]归程

并查集 倍增 最短路


题面

题面太长,难以概述,戳我

解析

如果考场上不是盲目地码码码,而是开始先认真思考,我可以多得好多分呢

算法

堆优化预处理结点到各结点最短路。
然后从出发点出发,只走当时边权为的边(海拔高于水位线),取能走到点中离点最短距离即为答案。
本质上就是枚举由坐车转步行那个关键点,坐车无距离,步行距离取最短路,因而可以统计答案。
复杂度
注意,只要时间允许,一定要在开头(每组数据开始)清空所有相关数组!!!

  1. il void dfs(re int u,re int g)
  2. {
  3. ans[0]=min(ans[0],dis[u]);
  4. for(re int i=h[u];i+1;i=e[i].nxt)
  5. {
  6. re int v=e[i].to;
  7. if(vis[v]||e[i].g<=g) continue;vis[v]=1;
  8. dfs(v,g);
  9. }
  10. }
  11. il void solve2()
  12. {
  13. fp(i,1,qq)
  14. {
  15. re int v=gi(),p=gi();
  16. v=(v+k*las-1)%n+1;p=(p+k*las)%(s+1);
  17. ans[0]=2e9;
  18. fp(j,1,n) vis[j]=0;vis[v]=1;
  19. dfs(v,p);
  20. printf("%d\n",ans[0]);las=ans[0];vis[v]=0;
  21. }
  22. }

算法

离线意味着可以改变询问顺序。
怎么改变呢?
我们思维上可能更习惯于水位线上升。
水位线上升时,有边权的边数逐渐增多,我们应该加边。然而加边后怎么办?从出发点出发怎么走?总不能忽略那些边权为的边吧。这是一条死路。
于是倒过来看。
水位线下降时,边权一一变为,意味着两个端点可以合并视为一个点,两个端点到号点的距离也可以取表现为取距近的点为父亲)。该操作用并查集维护。

这样,把询问按水位线从高到低排序,把边按海拔从高到低排序,如果水位线降到海拔以下就把边的两端点并入并查集,即可做到复杂度。

  1. il int find(re int x){return f[x]==x?x:f[x]=find(f[x]);}
  2. il void Merge(re int x,re int y)
  3. {
  4. re int fx=find(x),fy=find(y);
  5. if(fx==fy) return;
  6. if(dis[fx]>dis[fy]) f[fx]=fy;else f[fy]=fx;
  7. }
  8. il void solve1()
  9. {
  10. fp(i,1,n) f[i]=i;
  11. fp(i,1,qq)
  12. {
  13. re int v=gi(),p=gi();
  14. v=(v-1)%n+1;p=p%(s+1);
  15. q[i]=(que){v,p,i};
  16. }
  17. sort(q+1,q+1+qq);
  18. sort(a+1,a+1+m);
  19. re int now=2e9,ysn=1;
  20. fp(i,1,qq)
  21. {
  22. if(now>q[i].g)
  23. {
  24. now=q[i].g;
  25. while(a[ysn].g>now)
  26. {
  27. Merge(a[ysn].u,a[ysn].v);ysn++;
  28. }
  29. }
  30. ans[q[i].id]=dis[find(q[i].v)];
  31. }
  32. fp(i,1,qq) printf("%d\n",ans[i]);
  33. }

算法

树的意义是树上任意两点路径唯一。
为根节点,我们只要从出发点一直向上走父亲就可以了,遇到一个有边权的就停下来答该点
但是暴跳复杂度上限很虚。
于是倍增优化一下,维护父亲和区间内最小值(新技能)即可。
话说回来,维护区间内最小值就是这样。


在区间和区间中取,即可得到区间的最小值。
我们只倍增跳区间最小值为的区间即可。
复杂度

  1. il void dfss(re int u,re int fa)
  2. {
  3. for(re int i=h[u];i+1;i=e[i].nxt)
  4. {
  5. re int v=e[i].to;
  6. if(v==fa) continue;
  7. dfss(v,u);
  8. ff[0][v]=u;
  9. ff1[0][v]=e[i].g;
  10. }
  11. }
  12. il void solve3()
  13. {
  14. dfss(1,0);
  15. fp(i,1,18) fp(j,1,n) ff[i][j]=ff[i-1][ff[i-1][j]],ff1[i][j]=min(ff1[i-1][j],ff1[i-1][ff[i-1][j]]);
  16. fp(i,1,qq)
  17. {
  18. re int v=gi(),p=gi();
  19. v=(v+k*las-1)%n+1;p=(p+k*las)%(s+1);
  20. fq(i,18,0) if(ff[i][v]&&ff1[i][v]>p) v=ff[i][v];
  21. printf("%d\n",dis[v]);las=dis[v];
  22. }
  23. }

在上述提高组操作和数据分治后,即可获得的高分。。。

算法

先咕着。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注