1.23山师训练赛补题

C - 【The__Flash】的电影

  • 题目描述

Moscow is hosting a major international conference, which is attended by n scientists from different countries. Each of the scientists knows exactly one language. For convenience, we enumerate all languages of the world with integers from 1 to 109.
In the evening after the conference, all n scientists decided to go to the cinema. There are m movies in the cinema they came to. Each of the movies is characterized by two distinct numbers — the index of audio language and the index of subtitles language. The scientist, who came to the movie, will be very pleased if he knows the audio language of the movie, will be almost satisfied if he knows the language of subtitles and will be not satisfied if he does not know neither one nor the other (note that the audio language and the subtitles language for each movie are always different).
Scientists decided to go together to the same movie. You have to help them choose the movie, such that the number of very pleased scientists is maximum possible. If there are several such movies, select among them one that will maximize the number of almost satisfied scientists.

  • 输入

The first line of the input contains a positive integer n (1 ≤ n ≤ 200 000) — the number of scientists.
The second line contains n positive integers a1, a2, …, an (1 ≤ ai ≤ 109), where ai is the index of a language, which the i-th scientist knows.
The third line contains a positive integer m (1 ≤ m ≤ 200 000) — the number of movies in the cinema.
The fourth line contains m positive integers b1, b2, …, bm (1 ≤ bj ≤ 109), where bj is the index of the audio language of the j-th movie.
The fifth line contains m positive integers c1, c2, …, cm (1 ≤ cj ≤ 109), where cj is the index of subtitles language of the j-th movie.
It is guaranteed that audio languages and subtitles language are different for each movie, that is bj ≠ cj.

  • 输出

Print the single integer — the index of a movie to which scientists should go. After viewing this movie the number of very pleased scientists should be maximum possible. If in the cinema there are several such movies, you need to choose among them one, after viewing which there will be the maximum possible number of almost satisfied scientists.
If there are several possible answers print any of them.

  • 样例输入

3
2 3 2
2
3 2
2 3
6
6 3 1 1 3 7
5
1 2 3 4 5
2 3 4 5 1

  • 样例输出

2
1

  • 提示

In the first sample, scientists must go to the movie with the index 2, as in such case the 1-th and the 3-rd scientists will be very pleased and the 2-nd scientist will be almost satisfied.
In the second test case scientists can go either to the movie with the index 1 or the index 3. After viewing any of these movies exactly two scientists will be very pleased and all the others will be not satisfied.

  • 题意

科学家们各自会各自的语言,他们决定一起看一部电影,能听懂会很开心,能看懂字幕会比较开心,选择一部电影使得很开心的人最多,如果很开心的人数相同就选比较开心的人多的。

  • 解题思路

主要就是一个离散化,因为只有200000个点,点在1-1e9这么大的一个区间上是非常稀疏的,因此我们只需要考虑点之间的相对大小重新排序并确定新的位置。
因此离散化主要可分为三步:
1.用一个数组储存原数组的数据。
2.去除数组中重复的数据,因为相同的数据排序所在的位置是相同的。
3.最后通过二分快速查找第一个>=所查找的数的位置。
我在网上找到了两种版本的板子,第二个我感觉好理解一些,但写起来比较麻烦,结构体也不如数组模拟快。。
我还在acwing找到了个一模一样的题目,那题我用map怼过去了hh,但cf200多个数据点掐死了我用map偷题的念想。。卡在132号点超时了。。这也告诉我们不能抱侥幸心理指望数据水偷题。。当时要是认真学了离散化也不至于比赛做不出来。。QAQ

  • 模板
#include<algorithm>
struct Node {
	int data , id;
	bool operator < (const Node &a) const {
		return data < a.data;
	}
};
Node num[MAXN];
int rank[MAXN] , n;
for(int i=1; i<=n; i++) {
	scanf("%d",&num[i].data);//输入数据
	num[i].id = i;//记录原来所处位置
}
sort(num+1 , num+n+1);//按大小的相对关系排序
for(int i=1; i<=n; i++)
	rank[num[i].id] = i;//重新记录所处位置
————————————————
版权声明:本文为CSDN博主「weixin_43061009」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43061009/article/details/82083983
  • 代码
#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 2e5 + 10, M = N * 3;

int n,m,len;
int a[N],b[N],c[N];
int d[M];
int num[N];

int find_num(int x) {
	return lower_bound(d + 1,d + len + 1,x) - d;
}

int main() {
	cin >> n;
	for(int i = 1;i <= n;i++) {
		scanf("%d", &a[i]);
	}
	cin >> m;
	for(int i = 1;i <= m;i++) {
		scanf("%d", &b[i]);
	}
	for(int i = 1;i <= m;i++) {
		scanf("%d", &c[i]);
	}
	for(int i = 1;i <= n;i++) {
		d[++len] = a[i];
	}
	for(int i = 1;i <= m;i++) {
		d[++len] = b[i];
	}
	for(int i = 1;i <= m;i++) {
		d[++len] = c[i];
	}
	sort(d + 1,d + 1 + len);
	len = unique(d + 1, d + 1 + len) - d - 1;
	for(int i = 1;i <= n;i++) {
		num[find_num(a[i])]++;
	}
	int cnt = 1;
	int t1,t2;
	int maxn = 0,maxv = 0;
	for(int i = 1;i <= m;i++) {
		t1 = num[find_num(b[i])];
		t2 = num[find_num(c[i])];
		if(t1 > maxn || t1 == maxn && t2 > maxv) {
			maxn = t1;
			maxv = t2;
			cnt = i;
		}
	}
	cout << cnt << endl;
	return 0;
}

D - 【The__Flash】的排序

  • 题目描述

John has n tasks to do. Unfortunately, the tasks are not independent and the execution of one task is
only possible if other tasks have already been executed.

  • 输入

The input will consist of several instances of the problem. Each instance begins with a line containing
two integers, 1 ≤ n ≤ 100 and m. n is the number of tasks (numbered from 1 to n) and m is the
number of direct precedence relations between tasks. After this, there will be m lines with two integers
i and j, representing the fact that task i must be executed before task j.
An instance with n = m = 0 will finish the input.

  • 输出

For each instance, print a line with n integers representing the tasks in a possible order of execution.

  • 样例输入

5 4
1 2
2 3
1 3
1 5
0 0

  • 样例输出

1 4 2 5 3

  • 提示

  • 题意

拓扑排序

  • 解题思路

这题是个拓扑排序题。。拓扑排序之前知道但没学过正好学一波,拓扑排序主要分成两个大块一个是图的邻接表存储,一个就是根据图的入度进行拓扑排序。
首先先来看第一块图的存储,图的存储主要有两种方法,最常用的是用链表进行存储,用二重数组空间复杂度太大,一般1000以下可以考虑二重数组。
举个例子如图:
在这里插入图片描述
领接表的存储方式是通过链表把每一个点的路记录下来
1 -> 3 -> 4 -> 空
2 -> 1 - > 4 -> 空
3 -> 4 -> 空
4 -> 空
这样子就把这个图存到了链表当中。
然后就需要进行最为关键的拓扑排序了,在这之前需要先对有向图的两个重要性质有所了解那就是入度和出度。
同样举个例子记一下
在这里插入图片描述
在这里插入图片描述
入度就是指向这个点的路有多少条,出度就是这个点分出去多少条路。
知道这两个概念后,我们来看一下入度为0的点有什么性质,我们可以发现入度为0的点没有任何一个点指向它,也就是说它可以排在任何点的前面。比如这里1,2,3中的1它可以排在2,3的前面。根据这个性质我们可以分如下几步完成拓扑排序。
1.将所有入度为0的点放入队列。
2.当队列不空时,枚举队头点的所有出去的路,并令它的出度减一,因为不管后面怎么样他都是放在最前面的,因为没有点指向它。
3.当枚举完所有路后,就将点放入队列。
最后输出队列就是答案。
这题还有一个问题就是多组输入,因此没有办法使用数组模拟链表和队列,否则大量输入会让数组出现问题,这里必须使用结构体和STL中的队列,但数组模拟的板子还是得记一下,毕竟数组还是快啊。。不是多组输入还是用数组稳一点。。

  • 数组模拟链表、队列的拓扑排序模板(y总的板子,每次看都要感叹一下y总代码的牛逼和排版工整)
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n, m;
int h[N], e[N], ne[N], idx;
int d[N];
int q[N];

void add(int a, int b)//数组模拟邻接表
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

bool topsort()
{
    int hh = 0, tt = -1;

    for (int i = 1; i <= n; i ++ )//枚举所有入度为0的点
        if (!d[i])
            q[ ++ tt] = i;

    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];//枚举所有出路
            if (-- d[j] == 0)//枚举完后入队
                q[ ++ tt] = j;
        }
    }

    return tt == n - 1;
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);//表头初始化

    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);

        d[b] ++ ;//入度加1
    }

    if (!topsort()) puts("-1");
    else
    {
        for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);
        puts("");
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/47106/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 代码
#include<bits/stdc++.h>

using namespace std;

const int N = 105, M = N * N;

int head[N],d[N];
int n,m;
int cnt;
int a[N];
queue<int> tp;

struct Node {
	int v;
	int next_v;
}Map[M];

void add(int a,int b) {
	Map[cnt].v = b;
	Map[cnt].next_v = head[a];
	head[a] = cnt++;
}

void topsort() {
	queue<int> q;
	for(int i = 1;i <= n;i++) {
		if(!d[i]) {
			q.push(i);
			tp.push(i);
		}
	}
	while(!q.empty()) {
		int t = q.front();
		q.pop();
		for(int i = head[t];i != -1;i = Map[i].next_v) {
			int temp = Map[i].v;
			d[temp]--;
			if(!d[temp]) {
				q.push(temp);
				tp.push(temp);
			}
		}
	}
}

int main() {
	while(cin >> n >> m) {
		if(n == 0 && m == 0) {
			break;
		}
		cnt = 0;
		memset(head, -1, sizeof(head));
		memset(d, 0, sizeof(d));
		int a,b;
		for(int i = 0;i < m;i++) {
			scanf("%d%d", &a,&b);
			add(a,b);
			d[b]++;
		}
		topsort();
		bool flag = 0;
		while(!tp.empty()) {
			if(flag) {
				printf(" ");
			}
			printf("%d", tp.front());
			tp.pop();
			flag = 1;
		}
		printf("\n");
	}
	return 0;
}

K - 【The__Flash】的牛牛

  • 题目描述

Farmer John’s farm consists of a long row of N (1 <= N <= 100,000)fields. Each field contains a certain number of cows, 1 <= ncows <= 2000.
FJ wants to build a fence around a contiguous group of these fields in order to maximize the average number of cows per field within that block. The block must contain at least F (1 <= F <= N) fields, where F given as input.
Calculate the fence placement that maximizes the average, given the constraint.

  • 输入
  • Line 1: Two space-separated integers, N and F.
    Lines 2…N+1: Each line contains a single integer, the number of cows in a field. Line 2 gives the number of cows in field 1,line 3 gives the number in field 2, and so on.
  • 输出
  • Line 1: A single integer that is 1000 times the maximal average.Do not perform rounding, just print the integer that is 1000*ncows/nfields.
  • 样例输入

10 6
6
4
2
10
3
8
5
9
4
1

  • 样例输出

6500

  • 提示

  • 题意

约翰有许多牛,他至少要把牛群分成F块,求最大平均值。

  • 解题思路

本题巧妙的结合了二分,前缀和和双指针这几个知识点,非常好的一道综合题,每个点单拎出来都不难,但组合到一起就比较难想了。
本题很容易想到单调队列里的滑动窗口类题目,但要注意题目给的是至少N块地,因此这个窗口的长度不是固定的。。我当时想到这里就扑街了。。我选择放弃。。
后来看了题解,真的长知识了。。这题的入手点还是在于二分那么怎么想呢分一下几步:
1.我们假设最优解是x,二分的中点mid <= x,那么一定至少有一段平均值大于等于mid,这样我们就成功找到了可以二分的性质。如果我们找到了一段长度不小于F且平均值大于等于当前mid的区间就可以保证mid之后的区间还有大于等于mid的。
2.接下来就是平均数和前缀和的处理,我们引入标准差的概念就不难发现每个数减去平均数后,如果一段数的和大于0就说明这段平均数大于总平均数,于是我们可以先把每个数减去总平均数,最后再用双指针用minc记录最小的区间再进行前缀和,这样就可以遍历所有情况,并且时间复杂度为nlogn。

  • 代码
#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn = 1e5 + 10;

int cow[maxn];
double sum[maxn];
int n,m;

bool check(double avg) {
	for(int i = 1;i <= n;i++) {
		sum[i] = sum[i - 1] + (cow[i] - avg);
	}
	double minc = 0;
	for(int i = 0,j = m;j <= n;i++,j++) {
		minc = min(minc,sum[i]);
		if(sum[j] - minc >= 0) return true;
	}
	return false;
}

int main() {
	cin >> n >> m;
	double l = 0,r = 0;
	for(int i = 1;i <= n;i++) {
		scanf("%d", &cow[i]);
		r = max(r,(double)cow[i]);
	}
	while(r - l > 1e-5) {
		double mid = (l + r) / 2;
		if(check(mid)) l = mid;
		else r = mid;
	}
	printf("%d\n", (int)(r * 1000));
	return 0;
}

L - 【The__Flash】的鲨鲨

  • 题目描述

Today, Wet Shark is given n bishops on a 1000 by 1000 grid. Both rows and columns of the grid are numbered from 1 to 1000. Rows are numbered from top to bottom, while columns are numbered from left to right.
Wet Shark thinks that two bishops attack each other if they share the same diagonal. Note, that this is the only criteria, so two bishops may attack each other (according to Wet Shark) even if there is another bishop located between them. Now Wet Shark wants to count the number of pairs of bishops that attack each other.

  • 输入

The first line of the input contains n (1 ≤ n ≤ 200 000) — the number of bishops.
Each of next n lines contains two space separated integers xi and yi (1 ≤ xi, yi ≤ 1000) — the number of row and the number of column where i-th bishop is positioned. It’s guaranteed that no two bishops share the same position.

  • 输出

Output one integer — the number of pairs of bishops which attack each other.

  • 样例输入

5
1 1
1 5
3 3
5 1
5 5
3
1 1
2 3
3 5

  • 样例输出

6
0

  • 提示

In the first sample following pairs of bishops attack each other: (1, 3), (1, 5), (2, 3), (2, 4), (3, 4) and (3, 5). Pairs (1, 2), (1, 4), (2, 5) and (4, 5) do not attack each other because they do not share the same diagonal.

  • 题意

对角线上的点互相组合。

  • 解题思路

这题主要就是找主对角线和副对角线的规律,山师题解中的那两张图片一下子就把规律捋清了,这种题目还是把矩阵列一下好找规律。。
在这里插入图片描述
在这里插入图片描述

  • 代码
#include<iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

const int maxn = 2010;

typedef long long ll;

ll a[maxn];
ll b[maxn];

int main() {
	int n;
	cin >> n;
	int x,y;
	for(int i = 1;i <= n;i++) {
		scanf("%d%d", &x,&y);
		a[x + y]++;
		b[x - y + 1000]++;
	}
	ll sum = 0;
	for(int i = 1;i <= 2000;i++) {
		sum += a[i] * (a[i] - 1) / 2;
		sum += b[i] * (b[i] - 1) / 2;
	}
	cout << sum << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值