第一次参加NOI,当然,我没去现场做,只是在网络同步赛做了而已。

那网站,特别特别卡啊……

最后只交了第一题,原本认为能AC,但是因为某些原因只有50分。

我这可怜的第一次啊……

题目

题目点此处下载,密码:ghn5

思考过程

看完了所有题,就来思考第一题。

曾经的GDKOI和GDOI给我留下了不可磨灭的印象:感觉上这些题似乎都只能靠水分。

所以,我从一开始就想着打水法。

想着想着,感觉可以打个80分。好开心!

开打。

打了4000多byte,完成得差不多了,突然发现,啊,我想到正解了!!!

把这个4000多byte的程序弃掉,思考了一阵子,然后开打。

打了好久,有3000多byte,尴尬的是,过了前四个样例,却,被第五个样例卡掉了。

我好慌,好慌……

然后发现,我程序跑得慢的原因,是最短路花的时间太多了。

一世英名,卡在了最短路上。

我看看那个TLE掉的Dijsktra,不信邪,打了个SPFA。

依然被卡,于是,我决定重打一遍Dijsktra!

这一次,秒过……

然后我就愉快地交了程序……

解法

题目的意思是在图中,先走一段没有积水的路径,这些代价是免费的,然后在走到终点。

先想想该怎么搞离线的做法。

首先,肯定要求一遍最短路。

把问题转化一下,现在有积水的边不能走,你只能走没有积水的边。

答案即是你可以走到的的点中,最短路值最小的。

立体化地想一想,如果一开始没有水,然后雨越下越大,水位逐渐升高。

原本图是一个联通块,然后随着一些边的淹没,被逐渐分成了许多小的联通块。

不妨反过来想想。

可以做一遍最大生成树,维护联通块中最短路的最小值,在做的过程中统计答案。

这就是离线做法。

怎么在线呢?

维护联通块中最大值中,我用的是并查集。

既然非要在线,那就用可持久化并查集!!!

那样就可以询问历史版本,就可以在线做了,哈哈哈哈哈哈哈哈哈……

如果点x" role="presentation">xx所表示的集合要并到y" role="presentation">yy所表示的集合中时,在x" role="presentation">xx上记录一下两者联通的最高海拔。

并试着更新一下y" role="presentation">yy上维护的,表示联通块中最短路的最小值。

然而,我们需要记录一下历史版本。怎么记录?

直接暴力记录啊!反正最大生成树中,一共会连N−1" role="presentation">N−1N−1条边,那么,顶多更新N−1" role="presentation">N−1N−1次。

在每个点上存它的历史状态,直接简单粗暴的在一个数组里记录一条信息,表示某个点,在某个水位时,最短路最小值是多少。

搞完最大生成树后,排个序,然后一段区间对应的就是一个点的修改信息。

那么在询问时,先在并查集上不断往上跳,直到跳不了(再跳就被水淹没了)。

然后在这个点的修改信息中,二分出来,得出答案。

时间复杂度:

O(Nlg⁡N(Dijsktra)+(Mlg⁡M+Mlg⁡N)(最大生成树)+Nlg⁡N(排序)+Qlg⁡N(询问))=O(Mlg⁡M+(N+M+Q)lg⁡N)" role="presentation">O(NlgN(Dijsktra)+(MlgM+MlgN)(最大生成树)+NlgN(排序)+QlgN(询问))=O(MlgM+(N+M+Q)lgN)O(Nlg⁡N(Dijsktra)+(Mlg⁡M+Mlg⁡N)(最大生成树)+Nlg⁡N(排序)+Qlg⁡N(询问))=O(Mlg⁡M+(N+M+Q)lg⁡N)

代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 200000
#define MAXM 400000
int n,m;
struct edge
{
int u,v,l,a;
} ed[MAXM+1];
bool cmped(const edge &x,const edge &y)
{
return x.a>y.a;
}
struct EDGE
{
int to,l,a;
EDGE *las;
} e[MAXM*2];
int ne;
EDGE *last[MAXN+1];
void link(int u,int v,int l,int a)
{
e[ne].to=v,e[ne].l=l,e[ne].a=a;
e[ne].las=last[u];
last[u]=e+ne;
++ne;
}
long long dis[MAXN+1];
void Shortest_Path();
int Q,K,S;
struct Change_List
{
int x,a;
long long ans;
} cl[MAXN*2+1];
int ncl;
bool cmpcl(const Change_List &u,const Change_List &v)
{
return u.x<v.x || u.x==v.x && (u.a>v.a || u.a==v.a && u.ans>v.ans);
}
int ac[MAXN+1];
struct Union_Find_Set
{
int fa;
int a;
long long ans;
int dep;
} ufs[MAXN+1];
int getfa(int x)
{
while (ufs[x].fa!=x)
x=ufs[x].fa;
return x;
}
int main()
{
// freopen("return.in","r",stdin);
// freopen("return.out","w",stdout);
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
int T;
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m);
memset(last,0,sizeof last);
ne=0;
for (int i=1;i<=m;++i)
{
int u,v,l,a;
scanf("%d%d%d%d",&u,&v,&l,&a);
link(u,v,l,a);
link(v,u,l,a);
ed[i].u=u,ed[i].v=v,ed[i].l=l,ed[i].a=a;
}
scanf("%d%d%d",&Q,&K,&S);
Shortest_Path();
sort(ed+1,ed+m+1,cmped);
for (int i=1;i<=n;++i)
{
ufs[i].fa=i;
ufs[i].a=S+1;
ufs[i].ans=dis[i];
ufs[i].dep=1;
}
ncl=0;
for (int i=1;i<=n;++i)
{
++ncl;
cl[ncl].x=i;
cl[ncl].ans=dis[i];
cl[ncl].a=S+1;
}
for (int i=1;i<=m;++i)
{
int x=getfa(ed[i].u),y=getfa(ed[i].v);
if (x!=y)
{
//x->y
if (ufs[x].dep>ufs[y].dep)
swap(x,y);
ufs[x].fa=y;
ufs[x].a=ed[i].a;
ufs[y].dep=max(ufs[y].dep,ufs[x].dep+1);
if (ufs[x].ans<ufs[y].ans)
{
ufs[y].ans=ufs[x].ans;
++ncl;
cl[ncl].x=y;
cl[ncl].ans=ufs[x].ans;
cl[ncl].a=ed[i].a;
}
}
}
sort(cl+1,cl+ncl+1,cmpcl);
for (int i=1,j=0;i<=ncl;++i)
if (cl[i-1].x!=cl[i].x)
ac[j++]=i-1;
ac[n]=ncl;
long long lastans=0;
while (Q--)
{
int v,p;
scanf("%d%d",&v,&p);
v=(v+K*lastans-1)%n+1;
p=(p+K*lastans)%(S+1);
while (v!=ufs[v].fa && p<ufs[v].a)
v=ufs[v].fa;
int l=ac[v-1]+1,r=ac[v],res=-1;
while (l<=r)
{
int mid=l+r>>1;
if (p<cl[mid].a)
l=(res=mid)+1;
else
r=mid-1;
}
lastans=cl[res].ans;
printf("%lld\n",lastans);
}
}
return 0;
}
bool cmph(int son,int fa)
{
return dis[son]>dis[fa];
}
int h[MAXN],nh;
bool bz[MAXN+1];
void Shortest_Path()
{
memset(dis+1,127,sizeof(long long)*n);
memset(bz+1,0,sizeof(bool)*n);
dis[1]=0;
nh=1;
h[0]=1;
bz[1]=1;
while (nh)
{
int top=h[0];
pop_heap(h,h+nh--,cmph);
for (EDGE *ei=last[top];ei;ei=ei->las)
if (dis[top]+ei->l<dis[ei->to])
{
dis[ei->to]=dis[top]+ei->l;
if (!bz[ei->to])
{
bz[ei->to]=1;
h[nh++]=ei->to;
push_heap(h,h+nh,cmph);
}
}
bz[top]=0;
}
}

Others

1、 我比赛时没切题,是因为数组开小了……

2、 其实这个程序还有改进的地方,比如,在这题中,一些边的海拔是相同的,但我在最大生成树是会记录多条修改信息,其实可以并在一起。改起来也简单,但我懒得改了。

NOI2018 Day1 归程(return)的更多相关文章

  1. loj#2718. 「NOI2018」归程

    题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...

  2. 【BZOJ5415】【NOI2018】归程(克鲁斯卡尔重构树)

    [NOI2018]归程(克鲁斯卡尔重构树) 题面 洛谷 题解 我在现场竟然没有把这道傻逼题给切掉,身败名裂. 因为这题就是克鲁斯卡尔重构树的模板题啊 我就直接简单的说一下把 首先发现答案就是在只经过海 ...

  3. 「NOI2018」归程

    「NOI2018」归程 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 >\(1\) 个节点. \(m\) 条边的无向连通图(节点的编号从 \( ...

  4. LOJ #2718. 「NOI2018」归程 Dijkstra+可持久化并查集

    把 $Noi2018$ day1t1 想出来还是挺开心的,虽然是一道水题~ 预处理出来 1 号点到其它点的最短路,然后预处理边权从大到小排序后加入前 $i$ 个边的并查集. 这个并查集用可持久化线段树 ...

  5. 【luoguP4768】【NOI2018】归程

    description 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 nn 个节点.mm 条边的无向连通图(节点的编号从 11 至 nn).我们依次用 l,a ...

  6. LOJ #2718. 「NOI2018」归程(Dijkstra + Kruskal重构树 + 倍增)

    题意 给你一个无向图,其中每条边有两个值 \(l, a\) 代表一条边的长度和海拔. 其中有 \(q\) 次询问(强制在线),每次询问给你两个参数 \(v, p\) ,表示在 \(v\) 出发,能开车 ...

  7. #2718. 「NOI2018」归程 kruskal重构树

    链接 https://loj.ac/problem/2718 思路 我们希望x所在的连通块尽量的大,而且尽量走高处 离线的话可以询问排序,kruskal过程中更新答案 在线就要用kruskal重构树 ...

  8. UOJ 393 【NOI2018】归程——可持久化并查集

    题目:http://uoj.ac/problem/393 题解:https://www.cnblogs.com/HocRiser/p/9368067.html 但过不了 UOJ 的 hack 数据.不 ...

  9. 洛谷 4768 LOJ 2718「NOI2018」归程

    [题解] 本题有多种做法,例如可持久化并查集.kruskal重构树等. kruskal重构树的做法是这样的:先把边按照海拔h从大到小的顺序排序,然后跑kruskal建立海拔的最大生成树,顺便建krus ...

随机推荐

  1. BIO、NIO、AIO入门认识

    同步.异步.阻塞.非阻塞概念理解. 同步: 比如在执行某个逻辑业务,在没有得到结果之前一直处于等待阻塞状态,得到结果后才继续执行 异步: 比如在执行某个逻辑业务,在没有得到结果可以去干其他的事情,等待 ...

  2. 【转载】浅谈Linux内存管理机制

    经常遇到一些刚接触Linux的新手会问内存占用怎么那么多? 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在 ...

  3. 携程的 Dubbo 之路

    本篇文章整理自董艺荃在 Dubbo 社区开发者日上海站的演讲. 缘起 携程当初为什么要引入 Dubbo 呢?实际上从 2013 年底起,携程内主要使用的就是基于 HTTP 协议的 SOA 微服务框架. ...

  4. DelphiHookApi(经典)

    论坛里有关于HOOK API的贴子, 但其实现在方式显示得麻烦, 其实现在拦截API一般不用那种方式, 大都采用inline Hook API方式.其实也就是直接修改了要拦截的API源码的头部,让它无 ...

  5. ssm下使用分页插件PageHelper进行分页

    1. 导入maven依赖: <dependency> <groupId>com.github.pagehelper</groupId> <artifactId ...

  6. JAVA基础_类加载器

    什么是类加载器 类加载器是Java语言在1.0版本就引入的.最初是为了满足JavaApplet需要.现在类加载器在Web容器和OSGI中得到了广泛的应用,一般来说,Java应用的开发人员不需要直接同类 ...

  7. 19.SimLogin_case05

    # 使用自造的cookies登录马蜂窝 import requests from lxml import etree str = 'mfw_uuid=5bcfcc20-b235-fbbe-c1d6-a ...

  8. <scrapy爬虫>scrapy命令行操作

    1.mysql数据库 2.mongoDB数据库 3.redis数据库 1.创建项目 scrapy startproject myproject cd myproject 2.创建爬虫 scrapy g ...

  9. I-country

    I-country 在\(n\times m\)的网格图中,给出每个格子的权值,寻找有k个格子的凸联通块,使包含的权值最大,\(N,M≤15,K≤225\). 解 我们首先要知道凸联通块的定义 从任意 ...

  10. 扩展kmp板子

    using namespace std; #include <cstdio> #include <cstring> #include <algorithm> #de ...