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个数,求那个仅仅出现了一次的数
这题既然限制了空间,那么很自然地我们就得放弃“查找”或者类似的方法了。所以联想到刚学了不久的二进制的一些知识,用到了“异或”这个逆运算的概念:即,一个数a对于另一个数b只要异或两次,那么其结果的值依旧是a。所以异或n次的结果,自然就是最终只异或了一次的内个数了。
#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;
}




posted @ 2014-06-03 23:56  gaoxiang36999  阅读(197)  评论(0编辑  收藏  举报