The final five question form qhpMaster
这个。。按照很早之前的约定,东北赛结束之后的伪ACM之路就要靠自己去走了。所谓师傅领进门,修行在个人。
嘛~接下来就是这最后的五道题。
第一题,输入n,输出斐波那契的第n项的数。
首先你要知道一个数学知识。那就是求斐波那契数列的矩阵算法:
上式中,Fn即为斐波那契的第n项的数。
然后。。好说了,矩阵快速幂即可。
第二题,给你一棵空树(可能为多叉树),首先树上的所有节点都是空节点。然后你有如下两种操作,第一种,把它的所有儿子节点赋值为1;第二种,把它的所有父亲节点变为0。当有多组操作之后,询问某个节点是否为空。(操作数和查询数最大值均为500000)
对于这道题,是通过好几次一步步想的,主要还是见识太少,很多“显而易见”的东西自己却还根本不知道。
整理下用到的知识:
首先,是通过DFS的方式将一棵树遍历一次,并记录每个节点对应的进入时间和出树时间,根据此来将这棵树抽象成线性的结构。
其次,是对于刚抽象的线性结构采用线段树的方式进行维护。而第一种操作也就因此迎刃而解了,只要将入节点时间和出节点时间作为一整个区间赋值为1即可。
然后,对于第二种操作,我们只要将操作的内个节点赋值为0即可(对于如何查询我稍后解释)。这样操作2的时间复杂度便成了O(1)的。
最后,我们来解释下如何查询,对于已经记录过进入时间和出树时间的每个节点来说,倘若结果与区间长度不相等,即儿子节点中存在“被操作2了的空点”那么就证明该节点必然是空的。那么与此同时也就产生了一个问题,如果对于操作2,我们如果只改变一个点,那么当这个点后来又被操作1覆盖之后,而它的某些父亲节点本已被清空却无法用查询操作验证了怎么办呢?(这个问题比较绕口,没看懂的话仔细多读几遍吧) 一个比较巧妙地方法就是稍微改动下操作1,对于某个出入区间内已经存在“0”节点的父亲节点来说,先将该节点的父亲节点清空,然后再对该区间进行覆盖赋值“1”。
例如:
首先我对1号点进行操作1,那么区间【1,17】将赋值为1,即1到17号点均为1;
然后我对4号点进行操作2,那么点1,2,3,4将为0,但是我只把4号点改为0;
然后我对3号点进行操作1,那么点3,4将重新变为1,但这样再在查询的时候我就无法证明点1,2为0了,于是我就先把3号点的父亲节点——2号点变为0,然后将【3,4】变为1。
(题目链接:http://codeforces.com/contest/343/problem/D)
至于代码,今天刚把想法总结好,暂时还没写出来,回头写好了再贴上吧~
第四题,给你一个序列,这个序列里面有一个数仅仅出现了一次,剩下的数都出现了两次,现在我只给你1K的内存,一共100 W个数,求那个仅仅出现了一次的数
#include<iostream> using namespace std; int ans,a; int main(){ int n; cin>>n; cin>>ans; for(int i=1;i<n;i++){ cin>>a; ans^=a; } cout<<ans<<endl; }
第五题,还是给你一个100 w的序列,这个序列里面有两个数仅仅出现了一次,求那两个数,这回给你的时间让你无法在O(n log n)的时间内完成
这道题其实是上一道题的延伸,因为这题不过于限制空间,所以对于100W的序列我们可以尽情的开数组存了。首先,我们依旧是对所有数字进行按位异或,那么得到的结果就是两个只出现了一次的数之间的按位异或(这很容易想到,尤其是有了第四题的铺垫之后)。
我所想做的就是将所有的数分成两部分,每部分都只有一个你最终想得到的数。那么进而就可以转化为上一道题的思路了求得这两个数了。
接下来就是这道题的关键了,如何分成两部分呢?即,对于两个只出现过一次的数的异或的结果,如果二进制数中的某一位为1,那么就证明这两个数在这一位上一个为“0”、一个为“1”,于是,我们便可以以此为条件对所有的n个数进行分类。然后我们得到了两堆数,对每一堆数进行全异或也就分别得到了这两个结果。
#include<iostream> using namespace std; int a[1000010],b[1000010],c[1000010]; int ans,ans1,ans2; int main(){ int n; cin>>n; cin>>a[0]; ans=a[0]; for(int i=1;i<n;i++){ cin>>a[i]; ans^=a[i]; } int temp=ans,flag=0; while(1){ if((temp>>flag)&1) break; else flag++; } int j=0,k=0; for(int i=0;i<n;i++){ if((a[i]>>flag)&1) {b[j]=a[i];j++;} else {c[k]=a[i];k++;} } for(int i=0;i<j;i++) ans1^=b[i]; for(int i=0;i<k;i++) ans2^=c[i]; cout<<ans1<<" "<<ans2<<endl; }