1.Blah数集

题目描述

    大数学家高斯小时候偶然间发现一种有趣的自然数集合Blah,对于以a为基的集合Ba定义如下:

    (1)a是集合Ba的基,且a是Ba的第一个元素;

    (2)如果x在集合Ba中,则2x+1和3x+1也都在集合Ba中;

    (3)没有其他元素在集合Ba中了。

    现在小高斯想知道如果将集合Ba中元素按照升序排列,第N个元素会是多少?

输入

    输入包括一行两个数字,集合的基a(1<=a<=50))以及所求元素序号n(1<=n<=1000000)

输出

    输出一个正整数,集合Ba的第n个元素值

样例输入:                 样例输出:

1 100                         418

 

 

题目解析:

    显然该题是队列的使用。首先将a入队,然后每次将队首出队,并将队首拓展出来的数入队,直到找到第n小的元素。但是该题的拓展是2x+1及3x+1,所以有可能前一个数的3x+1比后几个数的2x+1要小,所以可能导致队列中不是从小到大,比如1、3、4,3拓展7和10,4拓展9和13,因为队列的特性,现在队列就变成了1、3、4、7、10、9、13,如果我们需要第5个数,应该是9而我们会取10,导致了错误。如果我们不断进行排序则复杂度过高;若我们依次找第几大数则不知道应该拓展到哪一步。

    第一种方法可以使用STL中的优先队列。在此不再描述,因为要尝试实现队列的操作。

    第二种方法即双重队列,或者说在同一个队列中,使用一个tail以及两个head,一个head只拓展2x+1,另一个只拓展3x+1,这样在保证队列递增的情况下光论某个head一定是递增的。那么每一步挑选哪一个head进行拓展,则取决于前者的2x+1和后者的3y+1哪个大,最终选择拓展小者。

    唯一一个特殊情况需要考虑,即若两者相同,为了避免重复拓展,可以考虑若相同,则拓展一个后两个head都进行出队操作(+1)。

 

 

C++代码(不使用STL):

#include <iostream>

using namespace std;

#define maxn 1000000

 

int min(int a, int b)

{

    if (a>b)

        return b;

    else

        return a;

}

 

void push(int queue[maxn], int head, int &tail, int x)

{

    if (head%maxn==(tail+2)%maxn)

        cout<<"overflow";

    else

    {

        tail++;

        tail=tail%maxn;

        queue[tail]=x;

    }

}

void pop(int queue[maxn], int &head, int tail, int &y)

{

    if (head%maxn==(tail+1)%maxn)

        cout<<"downflow";

    else

    {

        y=queue[head];

        head++;

        head=head%maxn;

    }

}

 

int main()

{

    int a,n;

    cin>>a>>n;

    int queue[maxn];

    int number=1;

    int x;

 

    int head2=0; int head3=0; int tail=-1;

    push(queue,0,tail,a);

    while (number<n)

    {

        int y;

        if (queue[head2]*2+1<queue[head3]*3+1)

        {

            pop(queue, head2, tail, y);

            push(queue, min(head2,head3), tail, y * 2 + 1);

            x=y*2+1;

        }

        else

        if (queue[head2]*2+1>queue[head3]*3+1)

        {

            pop(queue, head3, tail, y);

            push(queue, min(head2,head3), tail, y * 3 + 1);

            x=y*3+1;

        }

        else

        {

            pop(queue, head2, tail, y);

            pop(queue, head3, tail, y);

            push(queue, min(head2,head3), tail, y * 3 + 1);

            x=y*3+1;

        }

        number++;

    }

    cout<<x;

    return 0;

}

 

 

 

 

2.海港

题目描述

    小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。

    小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间ti (单位:秒),船上的乘客数ki,以及每名乘客的国籍 x(i,1), x(i,2),…,x(i,k);。

    小K统计了n艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的24小时(24小时=86400秒)内所有乘船到达的乘客来自多少个不同的国家。

    形式化地讲,你需要计算n条信息。对于输出的第i条信息,你需要统计满足 ti - 86400 < tp <= ti的船只p,在所有的x(p,j)中,总共有多少个不同的数。

输入

    第一行输入一个正整数n,表示小K统计了 n艘船的信息。

    接下来n行,每行描述一艘船的信息:前两个整数ti和ki分别表示这艘船到达海港的时间和船上的乘客数量,接下来ki个整数x(i,j)表示船上乘客的国籍。

    保证输入的ti是递增的,单位是秒;表示从小K第一次上班开始计时,这艘船在第 ti 秒到达海港。

输出

    输出n行,第i行输出一个整数表示第ii艘船到达后的统计信息。

样例输入:                 样例输出:

4                               3

1 4 1 2 2 3                  3

3 2 2 3                      3

86401 2 3 4                4

86402 1 5

对于100%的测试点,1≤n≤10^5,∑ki ≤3×10^5, 1≤xi,j≤10^5,1≤ti≤10^9。

 

题目解析:

    若使用朴素的想法,将所有读入的东西全部用二维数组保存起来再进行操作,则根据数据范围显然会爆空间。(因为每艘船人数不确定,只能每艘船都开3*10^5)但实际上总共只有3*10^5人,所以考虑以一维数组来储存人,并利用结构体或新数组来同时记录人到达的时间。于是我们就可以考虑使用队列来存储人(的国籍),对于船i,在将船中的乘客进队的同时,考虑将已经超时的(在队头)的乘客不断出队,就实现了队列中始终是当前船只所需要统计的乘客们。

    至于如何找到当前船只需要统计的乘客们来自多少不同的国家,若暴力穷举,则会导致超时。所以我们考虑使用hash1数组,对于hash1[i]=k,表示当前队列中国籍为i的有k个人。对该数组的维护即为:进队时将对应的hash1[j]加一,出队时将对应的减一。若某个位置减到了0,则当前国籍数-1。若某个位置从0加到了1,则当前国籍数加一。

 

 

C++代码(不使用STL):

#include <iostream>

using namespace std;

#define maxn 1000000

 

struct node

{

    int country;

    int time;

};

 

void push(node queue[maxn], int head, int &tail, node x)

{

    if (head%maxn==(tail+2)%maxn)

        cout<<"overflow";

    else

    {

        tail++;

        tail=tail%maxn;

        queue[tail]=x;

    }

}

void pop(node queue[maxn], int &head, int tail, node &y)

{

    if (head%maxn==(tail+1)%maxn)

        cout<<"downflow";

    else

    {

        y=queue[head];

        head++;

        head=head%maxn;

    }

}

 

int main()

{

    node queue[maxn];

    node y;

    int head=0; int tail=-1;

    int n; cin>>n;

    int ans=0;     //ans表示有效时间范围内不同国家的数量

    int hash[100100]={0};

    for (int i=1; i<=n; ++i)

    {

        node ck;     //乘客

        cin>>ck.time;

        int k; cin>>k;

        for (int j=1; j<=k; ++j)

        {

            cin>>ck.country;

            push(queue,head,tail,ck);

            if (hash[ck.country]==0)

                ans++;

            hash[ck.country]++;

            while (queue[head].time<=ck.time-86400)

            {

                pop(queue,head,tail,y);

                hash[y.country]--;

                if (hash[y.country]==0)

                    ans--;

            }

        }

        cout<<ans<<endl;

    }

    return 0;

}

 

 

 

 

3.家庭问题

题目描述

    有n个人,编号为1,2,……n,另外还知道存在K个关系。一个关系的表达为二元组(α,β)形式,表示α,β为同一家庭的成员。

    当n,k和k个关系给出之后,求出其中共有多少个家庭、最大的家庭中有多少人?

    例如:n=6,k=3,三个关系为(1,2),(1,3),(4,5)

    此时,6个人组成三个家庭,即:{1,2,3}为一个家庭,{4,5}为一个家庭,{6}单独为一个家庭,第一个家庭的人数为最多。

输入

    第一行为n,k二个整数(1≤n≤100)(用空格分隔)

    接下来的k行,每行二个整数(用空格分隔)表示关系

输出

    二个整数(分别表示家庭个数和最大家庭人数)

样例输入:                 样例输出:

6 3                            3 3

1 2

1 3

4 5

 

题目解析:

    将每个人视为一个结点,则“有关系”意味着两个结点相连。则求有多少家庭就是求这个图中有多少“连通块”。而最大的家庭中的人数即为最大的连通块有多少结点。我们对于每个人,可以使用广度优先搜索,搜出这个人所在的家庭所有的成员,并且使用hash1使得搜过的人再也不会被搜,以防止家庭数多加或是造成时间冗余。每次搜完一人后,即为找到了一个新家庭(因为若之前家庭已经搜过该人,则该人不会再被作为起点广度优先搜索)。每次在搜新家庭时,都将该家庭人数纪录下来,若超过当前家庭人数的最大值,则进行最大值的替换。

    最后将家庭的个数以及家庭人数的最大值输出。

 

 

C++代码(不使用STL):

#include <iostream>

#include <cstring>

using namespace std;

#define maxn 1000

 

void push(int queue[maxn], int &head, int &tail, int x)

{

    if (head%maxn==(tail+2)%maxn)

        cout<<"overflow";

    else

    {

        tail++;

        tail=tail%maxn;

        queue[tail]=x;

    }

}

void pop(int queue[maxn], int &head, int &tail, int &y)

{

    if (head%maxn==(tail+1)%maxn)

        cout<<"downflow";

    else

    {

        y=queue[head];

        head++;

        head=head%maxn;

    }

}

 

int main()

{

    int queue[maxn];

    int hash[110]={0};

    int family[110][110]={0};

    int sum=0;                  //sum表示家庭的个数

    int n,k; int head=0; int tail=-1;

    cin>>n>>k;

    for (int i=1; i<=k; ++i)

    {

        int x,y; cin>>x>>y;

        family[x][y]=1;

        family[y][x]=1;

    }

 

    int ans=-1;

    for (int i=1; i<=n; ++i)

        if (hash[i]==0)

        {

            sum++;

            hash[i]=sum;

            head=0; tail=-1;

            push(queue,head,tail,i);

            int s=1;

            while (head<=tail)

            {

                int y;

                pop(queue,head,tail,y);

                for (int j=1; j<=n; ++j)

                    if ((family[y][j]==1)&&(hash[j]==0))

                    {

                        push(queue,head,tail,j);

                        hash[j]=sum;

                        s++;

                    }

            }

            if (s>ans)

                ans=s;

        }

    cout<<sum<<' '<<ans;

    return 0;

}