这个东西还是应该写写博客的,毕竟IOI
题目连接已给出,直接点击大标题(推荐网络OJ:Universal Online Judge)
IOI2014 Rail
题目有点难看,耐心……
题目是说给出任意两段车站的距离(你只能得知其中的3(n?1)对距离)和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(n?1)下解决了这个问题
时间复杂度: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)其中x∈linku,y∈linkv且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 (u<v) u=v;
return ++a[u]==u;
}
(恶意缩行其实是不好的行为,小伙伴们不要学习啊╭(╯^╰)╮!)
这这这!这是怎么过的啊(大雾)?
嗯(⊙v⊙),我们来考虑一个这样的东西:树
我们尝试维护:每个点相对与编号比它小的点只有一条边
这样的话,整个图最后一定是一棵树
在最后一条边被询问之前,整个图的连通性是不确定的
时间复杂度:O(n)
(梅玉:健佳你太赖皮了,再也不和你玩了!)
(健佳:……)
IOI2014 Gondola
题意太长自己看……
Q1缆车序列检查
我们考虑一下原来的序列1∽n
这个序列可以循环变动
新的缆车可以在任意时刻换下任意一辆缆车
所以我们只需考虑一下最开始的缆车1∽n的相对位置是否有错误
嗯对就是这样
那如果最开始的缆车1∽n都被换下来了呢?
输出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类儿子(很显然这两个儿子相连)(注意每个人是按编号顺序以此进入)
嗯,一个dpofDP就解决了!
妈妈你看!人家的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,mid?1]和[mid+1,r]的函数值
因为决策单调性,两者的值域变为[a,r(mid)]和[r(mid),b],即枚举状态的减少
通过计算时间复杂度可知为O(nlog2n)
我们处理出四个函数的值,再进行各阶段天数的枚举
OK,最后一题就这样A了!
等等,我怎么MLE了?
请注意空间限制:64M
如果TLE了的话请注意一下你的线段树(少维护一些左右区间范围啊这种没用的东西)
时间复杂度:O(nlog2n)
嗯,IOI2014完结撒花!