IOI2014题解

这个东西还是应该写写博客的,毕竟IOI
题目连接已给出,直接点击大标题(推荐网络OJ:Universal Online Judge)

IOI2014 Rail

题目有点难看,耐心……
题目是说给出任意两段车站的距离(你只能得知其中的 3(n1) 对距离)和 0 车站的位置,让你确定每个车站的位置和类型 C D
距离的定义请看题
题目保证 0 车站为 C 类型

直接说正确的解法(部分分很水请独立思考)
我们会发现:
1.车站 x y 的距离 == 车站 y x 的距离
2.离距离某个 C 类型车站最近的车站一定是右方向上最近的 D 类型车站
根据以上两条,我们可以按照如下步骤操作:
1.求出所有其它车站到 0 号车站的距离,根据性质2我们可以推出距离最近的站点的位置及类型,将其记为 right
由此我们可以根据距离关系将剩下的车站分为两类:
1.位于 right 的左方,满足: dis(0,i)==dis(0,right)+dis(right,i)
2.位于 right 的右方,满足: dis(0,i)dis(0,right)+dis(right,i)

仔细观察发现,此时 0 right 只可能存在 C 类型的车站
就此我们又可以将 right 左方的车站分为两类:
1.位于 0 right 之间,满足 dis(right,i)dis(right,0)
2.位于 0 左方,满足 dis(right,i)>dis(right,0)

现在问题剩下: 0 左边与 right 右边的了
right 右边的为例:
思考:
1.将右边所有车站按照到 0 或者 right 排序,最近的一定是 D 类型的车站
对于剩下来的车站,我们维护一个 D 类型车站的栈
我们每次询问 i 到栈顶 top 的距离
我们可以通过计算得到 dis(0,top)+dis(top,i) dis(0,i)
Δ=(dis(0,top)+dis(top,i)dis(0,i))/2
计算位置 location[top]Δ 是否合法(在right右边)且该位置上是否存在一个 D 类型车站
如果合法且存在 D 类型车站,则说明位置 location[top]dis(top,i) 上存在一个 C 类型车站
否则,则说明位置 location[0]+dis(0,i) 上存在一个 D 类型车站,并将其加入栈
判断位置是否存在 D 类型车站,可以使用二分或者hash

上面这是干嘛呢?
其实仔细画个图看看就能明白
拿出笔和纸吧(^o^)/~
位置: 0   1  2   3  4  5  6
车站: C D C      D D
编号: 0  1   2       3  4
自己用笔和纸玩一玩就明白

0 左边的车站解法类似

这样我们就在限制条件 3(n1) 下解决了这个问题
时间复杂度: O(n) O(nlogn)

( ps: UOJ上是可以看到其他人的AC代码的
所以不会写的话可以去Orz一下)

IOI2014 Wall

题意很简单:
维护一个区间,支持两种操作:
1、将一段<=k的数全部变成k
2、将一段>=k的数全部变成k

线段树大法好啊!
神马你说你不会线段树打标记∑q|゚Д゚|p?

简单介绍一下: lazy 标记
意识流一下 lazy 这个词:懒!
把当前对于一个线段区间的操作暂时记录在这,即lazy
待到下一次访问该节点时,将标记操作实施,即向左右子节点下传
(如果不能明白,那就去自行百度下,像”算法竞赛入门经典训练指南”这类良心书上也有讲解)

对于这个题,维护一下标记和最大值、最小值即可
时间复杂度: O(nlogn)

IOI2014 Game

题意:给定询问点对的顺序,要求构造一张图,使得在所有询问执行完之前,对方无法判断整张图的连通性
两熊孩子玩游戏……

一个比较容易懂的做法

我们考虑一下每个询问 (u,v) 和它们所在的连通块 linku,linkv
linku linkv 之间除 (u,v) 这条边以外的所有边都被询问过,则回答存在
否则回答不存在
若存在另一条边 (x,y) 其中 xlinku,ylinkv x,y 没有被询问,若此时我们回答 (u,v) 之间存在一条边,此时 linku linkv 已经连通,那么当 (x,y) 这个询问被放在最后时,健佳就输了这场游戏
linku linkv 之间的边数为 |linku|×|linkv| (|G|表示G的点数)
连通块的维护可以使用并查集维护
唯一要注意的是并查集合并时询问次数的合并
时间复杂度:近似 O(n3) (其实远远不到)

一个比较容易写的做法

我的天哪!为什么我的快代码1K,人家代码100+B?

#include"game.h"
int a[1510];
void initialize(int) {}
int hasEdge(int u,int v)
{
if (ureturn ++a[u]==u;
}

(恶意缩行其实是不好的行为,小伙伴们不要学习啊╭(╯^╰)╮!)
这这这!这是怎么过的啊(大雾)?

嗯(⊙v⊙),我们来考虑一个这样的东西:
我们尝试维护:每个点相对与编号比它小的点只有一条边
这样的话,整个图最后一定是一棵树
在最后一条边被询问之前,整个图的连通性是不确定的
时间复杂度: O(n)
(梅玉:健佳你太赖皮了,再也不和你玩了!)
(健佳:……)

IOI2014 Gondola

题意太长自己看……

Q1缆车序列检查

我们考虑一下原来的序列 1n
这个序列可以循环变动
新的缆车可以在任意时刻换下任意一辆缆车
所以我们只需考虑一下最开始的缆车 1n 的相对位置是否有错误
嗯对就是这样
那如果最开始的缆车 1n 都被换下来了呢?
输出 1 ,完毕
( ps: 容许我吐槽一下子任务 1 ……)
( pps: 一定不要在这里使用缆车的hash,不然后果自负)
时间复杂度: O(nlogn) (排序)

Q2替换序列

根据上面的推论,我们模拟一下每辆缆车换上来的顺序:
若当前缆车存在于 gondolaSeq 中,则将其放在对应位置
否则放在任意一个位置上,满足当前情况下该位置 i 上的缆车与 gondolaSeq[i] 不同即可(即一个未确定的位置上)
为了方便起见,我们将这个任意位置定为 max(gondolaSeq[]) 所在的位置(即最后一辆缆车的所在位置)
时间复杂度: O(max(gondolaSeq[]))

Q3替换序列计数

MOD=1000000009
这个比上面两个有技术含量多了
增加了一种基于分治思想的快速计算方法(不是 FFT 而是快速幂……)
(O(∩_∩)O哈哈~我就不行你能把我的头压在键盘上fahsdglebangekjbalgbdska……)
MOD=1000000009
妈呀! max(inputSeq[]) 竟然有 1000000000 ,不能模拟了(@之前写了hash的朋友……)
我们将 inputSeq[] 排个序
inputSeq[] 上的数显然只能在最后放在一个固定位置
诶,我们会发现有些缆车先换上来又换下去了,而且答案主要就是他们玩大的!
等等!
在相邻的 inputSeq[] 之间,那些先换上来又换下去了的缆车,对答案的贡献是一样的,每个缆车可放的位置,为当前状态下剩下的未确定的位置个数
快速幂搞搞就A了!
MOD=1000000009
( ps: 如果不会写的话,可以去膜拜UOJ上其它Orz的代码)
MOD=1000000009,我都写了四遍了你还不取模自己看着办
时间复杂度: O(nlogn)? (不知道其实我不会分析……)

Gondola 完结撒花!

IOI2014 Friend

题目意思自己看……

啥?我没看错吧?一般图最大权独立点集不是NP-hard问题么(NP难题请自行百度)?
众所周知,NP-hard问题是没有精确算法的(说不定以后有哪位Orz搞出来了)
( ps: 谁说的?我有 O(2N) 的枚举!哎呀kasdlgaiognwagkjdn……)

如果你去想最大权独立点集问题,恭喜你掉坑里了!
我们不妨换一个思想:考虑 i host[i] ,我们将他们连一条边
这样我们就构成了一颗树
再把不同的连边方式作为不同的边权放在边上
嗯,这应该是树形DP
恭喜你猜中正解!

我们定义:
yes[i]——在以i为根的子树可以选择点i时的最大点权独立集
no[i]——在以i为根的子树不可以选择点i时的最大点权独立集
( ps: 可以选择 选择)
DP 方法如下:
1.若 i 不可以选择,即与其相连的 I 类儿子可以选择, M W 类儿子不可以选择(为什么 M 就不能选呢? i 不可以选择代表有个与它相连的人被选择,而 M 类儿子会与其连边,嘣!)
2.若 i 可以选择并且选择,即与其相连的 M 类儿子可以选择, I W 类儿子不可以选择(这个很容易理解)
3.若 i 可以选择但不选择,此时 i 对i的儿子没有多大影响,我们可以在这里再进行一个 dp ,这个 dp 的限制条件如下:
如果我们之前决策选择过一个编号较小的 I W 类儿子,就不能再决策徐州呢编号大的 M W 类儿子(很显然这两个儿子相连)(注意每个人是按编号顺序以此进入)
嗯,一个 dp of DP 就解决了!

妈妈你看!人家的AC代码怎么又比我短一截啊?

#include "friend.h"
#define max(a,b)    ((a)>(b)?(a):(b))
int     G[100005];
int     findSample(int N,int F[],int H[],int O[])
{
  for   (int i=N-1;i;i--)
    {
    int x=H[i];
    if  (O[i]==0)
        F[x]+=G[i],G[x]+=max(F[i],G[i]);
    if  (O[i]==1)
        F[x]=max(F[x]+max(F[i],G[i]),G[x]+F[i]),G[x]+=G[i];
    if  (O[i]==2)   
        F[x]=max(F[x]+G[i],G[x]+F[i]),G[x]+=G[i];
    }
    return  max(F[0],G[0]);
}

(只能说博主自带代码复杂度翻倍的BUFF,具体多少倍看题吧……)
大神:“嗯,很显然,这两个 DP 其实是等价的(ーー゛)”
博主:“我还是太弱了……”

IOI2014 Holiday

题目意思一样自己看nglajksdgnawn……

嗯,第一眼看完:不会o(>﹏<)o……
不过我们会发现,子任务2的起点是固定在最左的
嗯,对于子任务2,一定是一次性从左走到右,然后在路途中玩上几天
枚举走的天数+函数式线段树区间前k大值之和水过?
啥?函数式线段树是啥?
不会的自行百度吧……(你也可以使用主席树,没什么差别)

简单扯一两句:
假设我们要维护一颗线段树,它支持 ctrl+z (即还原历史记录)操作,怎么办?
我们可以通过新建节点来代替原先的修改操作,从而实现全信息保存
嗯,由于线段树单点修改每次只会影响一条链的值,改一条链即可
空间复杂度: O(n+mlogn)n 为序列长度, m 为操作次数
时间复杂度: O(nlogn)

思考:对于其他任务,我们每次一定只存在四种情况
1、一直向右,路上玩
2、先向右再向左,路上玩
3、先向左再向右,路上玩
4、一直向左,路上玩

我们回过头来看看子任务2
r[x] 表示向右边行动 x 天(包括走和玩)所到达的最右城市编号(不是景点数)
显然 x 天后我们停留在 r[x] 号城市上
我们会发现: r[x]<=r[x+1]
意识流:当天数增加一天时,我们可以选择在之前的城市中再多玩一天,或者多走一天到达下一个城市
意识流告诉我们,可以数学归纳证明,这里不再赘述

同理,另外三个函数值(向左边行动,向右边行动后折返回出发点,向左边行动后折返回)同样具有单调性

嗯,不错的想法
决策的单调性有什么用呢?

问题转化一下:
函数 r() 的定义域为 [l,r] ,值域为 [a,b] ,且满足单调上升,求 r()
我们定义 mid=(l+r)/2
我们先枚举 [a,b] 求出 r(mid) 的值
(处理值的方法:上面提到过)
利用决策的单调性,递归处理定义域为 [l,mid1] [mid+1,r] 的函数值
因为决策单调性,两者的值域变为 [a,r(mid)] [r(mid),b] ,即枚举状态的减少
通过计算时间复杂度可知为 O(nlog2n)

我们处理出四个函数的值,再进行各阶段天数的枚举
OK,最后一题就这样A了!

等等,我怎么MLE了?
请注意空间限制: 64M
如果TLE了的话请注意一下你的线段树(少维护一些左右区间范围啊这种没用的东西)
时间复杂度: O(nlog2n)

嗯,IOI2014完结撒花!

你可能感兴趣的:(各类比赛)