快排归并排练习题

目录

栗酱的连通图

小杰的签到题

过来站站队伍 

一堆点 

别看了 这是水题 

兔子的逆序对


栗酱的连通图

题目描述

萌萌哒栗酱有n个点,第i个点有点权ai(ai为偶数),你可以在任意两点之间添加一条边,每一条边的边权为连接它的两个点的点权之和除以2。
现在她需要添加n-1条边,使任意两点相互连通,并且连通后的边权和最大。

输入描述:

第一行一个数T,表示有T组数据。

对于每组数据,第一行输入一个数n,表示点的数量,

接下来一行输入n个数,a1,a2,…,an,其中ai表示第i个点的点权。

任意两个相邻数之间用空格隔开。

输出描述:

对于每一组数据,输出一个数,即最大边权和。

示例1

输入

2
5
4 2 4 4 2
10
10 2 6 4 6 8 10 8 2 10

输出

14
73

备注:

T≤10

1≤n≤103

1≤ai≤103,保证ai为偶数。

源代码

//本题看似连通图的算法,实际非常简单,简单的排序
//因为联通的点可以是任意的,我们只需要将所有的点(除最大值的点)与最大值联通
//累计计算边权和即可
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1000000 + 10;
int q[N];
void sort(int q[],int l,int r)
{
    if(l >= r)return;
    int x = q[l + r >> 1],i = l - 1,j = r + 1;
    while(i < j)
    {
        do i ++ ;while(q[i] < x);
        do j -- ;while(q[j] > x);
        if(i < j)swap(q[i],q[j]);
    }
    sort(q,l,j);
    sort(q,j + 1,r);
}
int main()
{
    int t;
    cin >> t;
    while(t -- )
    {
        memset(q,0,sizeof(q));
        int n;
        cin >> n;
        for(int i = 1;i <= n;i ++ )cin >> q[i];
        sort(q,1,n);
        int ans = 0;
        for(int i = 1;i < n;i ++ )
        {
            ans += (q[i] + q[n]) / 2;
        }
        cout << ans << endl;
    }
}

小杰的签到题

题目描述

小杰组织了一场比赛,在比赛前需要安排队伍签到,但他不确定签到要花多久时间,现在他来请求你的帮助。已知签到是在一个体育馆,该体育馆布置有三个桌子以供不同队伍的队伍同时签到,一个桌子最多只能有一支队伍签到,一支队伍只需在一张桌子前完成签到即可。如果三个桌子都有队伍在签到,其它需要签到的队伍就需要在任意一个桌子前排队,等待签到。

我们假设在t=0的时刻开始接受签到,n支队伍分别在a1,a2,...,an时刻到达体育馆,每支队伍完成签到均需b的时间,为使问题简单,我们忽略体育馆中移动的时间。你需要告诉小杰最早什么时刻,所有的队伍均签到完成。

输入描述:

多组读入。
输入数据的第一行是一个整数T,表示数据的组数。
每组数据的第一行是一个整数n,表示签到的队伍数。
接下来一行有n个整数ai,表示第i支队抵达体育馆的时间。
每组的最后一行是一个整数b,表示一支队伍完成签到的时间。

输出描述:

对于每组数据,输出最后一支队伍最早签到完成的时刻。

示例1

输入

2
5
1 2 4 5 7
4
7
4 4 4 2 8 9 11
5

输出

11
17

备注:

1≤n≤600

0≤ai≤104

1≤b≤1500

数据不超过250组

源代码 

//双端队列,三人一队,在每次进行入队之前若是队列已经满了,则进行判断
//若需要等待则推入队伍到达的时间加上等待的时间,若无需等待直接推入队伍的到达时间即可
//队列满了就队头加一,从而保证队列当中一直处于一个递增的趋势
//也就是单调队列,队列的最后一队即为其到达可以开始的时间,也就是最后一队
//队尾元素加上每队需要等待的时间即为这些队伍所用的最短时间
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1000000 + 10;
int a[N],q[N];
void sort(int q[],int l,int r)
{
    if(l >= r)return;
    int x = q[l + r >> 1],i = l - 1,j = r + 1;
    while(i < j)
    {
        do i ++ ;while(q[i] < x);
        do j -- ;while(q[j] > x);
        if(i < j)swap(q[i],q[j]);
    }
    sort(q,l,j);
    sort(q,j + 1,r);
}
int main()
{
	int t;
	cin >> t;
	while(t -- )
	{
		memset(a,0,sizeof(a));
		memset(q,0,sizeof(q));
		int n;
		cin >> n;
		for(int i = 1;i <= n;i ++ )cin >> a[i];
		sort(a,1,n);
		int m;
		cin >> m;
		int hh = 0,tt = -1;
		for(int i = 1;i <= n;i ++ )
		{
		    if(tt - hh == 2)
		    {
		        if(a[i] >= q[hh] + m)q[++ tt] = a[i];
		        else q[++ tt] = q[hh] + m;
		        hh ++ ;
		    }
		    else q[++ tt] = a[i];
		}
		cout << q[tt] + m << endl;
	}
    return 0;
}

过来站站队伍 

题目描述

在某个星球,每个人身上都有不一样的标号。一天,小娟在食堂排队时,发现后面老是喜欢插队,她看到很生气,拿出一张魔法卡,巴拉巴拉能量(魔仙变身,看多了动画片,hhh)。。。。。。。,将他们全部按照从小到大按照标号排好了。你能帮忙写出魔法卡内部的程序吗?写出来就给你一个气球。嘿嘿嘿

输入描述:

第一行,输入一个整数n(1<=n<=100000);
第二行,输出n个整数;
注意多组输入

输出描述:

输出n个数

示例1

输入

3
1 3 2

输出

1 2 3

示例2

输入

5
1 3 4 2 5

输出

1 2 3 4 5

源代码

#include <iostream>
using namespace std;
const int N = 1000000 + 10;
int q[N];
void sort(int q[],int l,int r)
{
    if(l >= r)return;
    int x = q[l + r >> 1],i = l - 1,j = r + 1;
    while(i < j)
    {
        do i ++ ;while(q[i] < x);
        do j -- ;while(q[j] > x);
        if(i < j)swap(q[i],q[j]);
    }
    sort(q,l,j);
    sort(q,j + 1,r);
}
int main()
{
    int n;
    cin >> n;
    for(int i = 1;i <= n;i ++ )cin >> q[i];
    sort(q,1,n);
    for(int i = 1;i <= n;i ++ )cout << q[i] << ' ';
    return 0;
}

一堆点 

题目描述

这个问题很简单,有n个三维点(x,y,z),要求按照它们距离原点的距离来排序,若距离相等,则按照x的顺序递增,若x也相等,则按照y递增的顺序递增(依次类推,不会输入重复点)

输入描述:

第一行输入一个正整数n(1<=n<=106),表示有n个三维点

随后n行,每行包含3个实数x,y,z表示一个点的坐标(均在double类型内)。

输出描述:

按照排序之后的结果,每行输出一个三维点的x y z方向的坐标,两个数之间以一个空格分隔

示例1

输入

4
0 0 0
1 1 1
3 3 3
-2 -2 -2

输出

0 0 0
1 1 1
-2 -2 -2
3 3 3

源代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Point
{
    double x;
    double y;
    double z;
    double d;
};
int cmp(const struct Point &A,const struct Point &B)
{
    if(A.d != B.d)return A.d < B.d;
    else if(A.x != B.x)return A.x < B.x;
    else if(A.y != B.y)return A.y < B.y;
    else return A.z < B.z;
}
vector<Point> a;
int main()
{
    int n;
    cin >> n;
    while(n -- )
    {
        Point t;
        cin >> t.x >> t.y >> t.z;
        t.d = t.x * t.x + t.y * t.y + t.z * t.z;
        a.push_back(t);
    }
    sort(a.begin(),a.end(),cmp);
    for(int i = 0;i < int(a.size());i ++ )cout << a[i].x << ' ' << a[i].y << ' ' << a[i].z << endl;
    return 0;
}

别看了 这是水题 

题目描述

把一个长度为N的数值数组按从小到大的顺序排序并输出

输入描述:

输入为两行 第一行为一个整型数字N 第二行输入N个整型数字num 代表数组里面的元素(每个元素之间用空格隔开)

输出描述:

输出为一行 即为数组从小到大排序后的结果 每个数字之间用空格隔开(行末没有空格)

示例1

输入

5
2 8 7 4 5

输出

2 4 5 7 8

示例2

输入

4
2 2 2 2

输出

2 2 2 2

备注:

0<N<1000

0<num<=10000

源代码

#include <iostream>
using namespace std;
const int N = 1000000 + 10;
int q[N];
void sort(int q[],int l,int r)
{
    if(l >= r)return;
    int x = q[l + r >> 1],i = l - 1,j = r + 1;
    while(i < j)
    {
        do i ++ ;while(q[i] < x);
        do j -- ;while(q[j] > x);
        if(i < j)swap(q[i],q[j]);
    }
    sort(q,l,j);
    sort(q,j + 1,r);
}
int main()
{
    int n;
    cin >> n;
    for(int i = 1;i <= n;i ++ )cin >> q[i];
    sort(q,1,n);
    for(int i = 1;i <= n;i ++ )cout << q[i] << ' ';
    return 0;
}

兔子的逆序对

题目描述

兔子最近喜欢上了逆序对。
一个逆序对(i,j) 需要满足 i < j 且 ai > aj
兔子觉得只是求一个序列的逆序对个数太没有意思了。
于是兔子想到了一个更有趣的问题!
兔子可以把区间[L,R] 反转,例如序列{1,2,3,4} 反转区间[1,3] 后是{3,2,1,4}。
兔子有m次反转操作,现在兔子想知道每次反转后逆序对个数是奇数还是偶数,兔子喜欢偶数,而讨厌奇数。
请注意,每一次反转操作都会对原序列进行改变。例如序列{1,2,3,4} 第一次操作区间[1,2] 后变成{2,1,3,4} 第二次反转区间[3,4] 后变成 {2,1,4,3}

输入描述:

第一行一个整数 n,表示序列的大小。
第二行 n 个整数ai 表示序列的各个元素。
第三行一个整数m,表示操作个数。
接下来 m 行,每行两个整数 l,r,表示反转的区间。

输出描述:

输出共m行每行一个字符串,表示反转后序列逆序对个数的奇偶性,如果是逆序对个数奇数,输出"dislike"(不含引号),如果是偶数,输出"like"。

示例1

输入

4
1 2 3 4
4
1 2
3 4
1 4
2 3

输出

dislike
like
like
dislike

说明

注意:以下的(i,j)指的是位置 i 和位置 j
a={2,1,3,4} 的逆序对是 (1,2) 共1个,1是奇数,所以是dislike
a={2,1,4,3} 的逆序对是 (1,2) (3,4)共2个,2是偶数,所以是like
a={3,4,1,2} 的逆序对是 (1,3) (1,4) (2,3) (2,4)共4个,4是偶数,所以是like
a={3,1,4,2} 的逆序对是 (1,2) (1,4) (3,4) 共3个,3是奇数,所以是dislike

备注:

对于20%的数据
1 ≤ n ≤ 100 
1 ≤ m ≤ 10 
对于40%的数据
1 ≤ n ≤ 2000 
1 ≤ m ≤ 50 
对于60%的数据
1 ≤ n ≤ 2000 
1 ≤ m ≤ 104
对于100%的数据
1 ≤ n ≤ 105 
1 ≤ m ≤ 2*106

对于所有数据 l ≤ r且 ai 是n的一个排列,即ai互不相同且ai ≤ n

由于读入数据较大,建议使用快速读入。

源代码

//本题我没有使用快读也过了,肯定使用快读之后速度会更加快
//基础思想在于基于分治的归并排序,逆序对的计算公式为ans = mid - i + 1
//分治即为不同二分元素,直至分至一个元素时结束
//在每次分的两个区间当中,若前一区间存在着大于后一区间的数
//由于在分治的同时也排序,因此每个区间的数都是升序的
//若是第一区间开头的某个数大于第二区间的数,也就意味着在第一区间当中该数后面的所有数都可以构成逆序对
//这也就是逆序对计算公式的又来
//在刚开始的时候,我们求取逆序队的个数,用于以后处理来累加逆序对个数
//观察规律我们可以发现逆序对个数的规律,就拿样例来说
//初始     0    
//第一次   1    (1,2)   1
//第二次   2    (3,4)   1
//第三次   4    (1,4)   2
//第四次   3    (2,3)   -1
#include <iostream>
using namespace std;
const int N = 2000000 + 10;
int q[N],tep[N],ans = 0;
void merge_sort(int q[],int l,int r)
{
    if(l >= r)return;
    int mid = l + r >> 1;
    merge_sort(q,l,mid);
    merge_sort(q,mid + 1,r);
    int i = l,j = mid + 1,k = 0;
    while(i <= mid && j <= r)
    {
        if(q[i] <= q[j])tep[k ++ ] = q[i ++ ];
        else 
        {
            tep[k ++ ] = q[j ++ ];
            ans += mid - i + 1;
        }
    }
    while(i <= mid)tep[k ++ ] = q[i ++ ];
    while(j <= r)tep[k ++ ] = q[j ++ ];
    for(int i = l,j = 0;i <= r;i ++ ,j ++ )q[i] = tep[j];
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;i ++ )cin >> q[i];
    int m;
    scanf("%d",&m);
    merge_sort(q,1,n);
    while(m -- )
    {
        int l,r;
        scanf("%d%d",&l,&r);
        ans += (r - l + 1) / 2;
        if(ans & 1)printf("dislike\n");
        else printf("like\n");
    }
    return 0;
}

留下一道[归并排序][树状数组][线段树]未补点我来补

猜你喜欢

转载自blog.csdn.net/couchpotatoshy/article/details/126355513
今日推荐