2020 China Collegiate Programming Contest Qinhuangdao Site

2020 China Collegiate Programming Contest Qinhuangdao Site

A.A Greeting from Qinhuangdao

题意: 组合数签到题

题解: c [ 2 ] [ r ] / c [ 2 ] [ r + b ] c[2][r] / c[2][r + b] c[2][r]/c[2][r+b]

代码:

#include <bits/stdc++.h>

using namespace std;

int const N = 1e3 + 10;
typedef long long LL;

int n, T, m;
int c[N][N], kase = 1;
void init() {
    for (int i = 0; i < N; ++i)
        for (int j = 0; j <= i; ++j)
            if (j == 0) c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]);
}

int gcd(int a,int b) {
	return b == 0? a: gcd(b, a % b);
}

int main() {
//    freopen("in.txt", "r", stdin);
    cin >> T;
    init();
    while(T--) {
    	cin >> n >> m;
//    	cout << n << " " << m << endl;
    	if (n == 1) {
    		printf("Case #%d: 0/1\n", kase++);
    		continue;
		}
//    	cout << n << " " << m << endl;
    	int a = c[n + m][2];
    	int b = c[n][2];
//    	cout <<a << " " << b << endl;
    	int t = gcd(a, b);
    	a /= t, b /= t;
    	printf("Case #%d: %d/%d\n", kase++, b, a);
	}
    return 0;
}

D.Exam Results

题意: 给定n个人,每个人有两个分数a和b。要求选择对于某个人选择一个分数x,然后计算出存在有一个分数大于x*p/100的学生分数的学生有多少个,同时要求选择出来的这个学生的分数是n个学生选出的分数最高的 。打印最多的学生数目。 ∑ n < = 5 ∗ 1 0 5 \sum_{}n <= 5 * 10^5 n<=5105

题解: 首先保证要选出n个学生,这个可以考虑使用双指针的思路处理,i~j的区间中包含了每个学生至少一个分数,然后通过调整i的位置来保证区间的范围内是所有合法的学生。 首先要保证选择到n个学生,因此先调整j的位置,使得刚好选择n个学生,然后回退一位,这样一旦j向右一位就能使得有n个学生。之后对于j的每一次向右移动,都需要调整一下左边界,不断更新答案。

代码:

#include <bits/stdc++.h>

using namespace std;

int const N = 5e5 + 10;
typedef long long LL;
typedef pair<int, int> PII;
int n, T, m, p, cnt[N], tot, kase = 1;
PII stu[N];

int main() {
    cin >> T;
    while(T--) {
    	tot = 0;
    	memset(cnt, 0, sizeof cnt);
    	scanf("%d%d", &n, &p);
    	for (int i = 1; i <= n; ++i) {
    		int a, b;
    		scanf("%d%d", &a, &b);
    		stu[tot++] = {a, i};
    		stu[tot++] = {b, i};
		}
		
		sort(stu, stu + tot);
		
		int now = 0, tail = -1;
		while(now != n) {  // 先要将n个人都选择进去 
			tail++; 
			cnt[stu[tail].second] ++;
			if (cnt[stu[tail].second] == 1) now ++;
		}
		
		// 然后留下最后一个人 
		now --;
		cnt[stu[tail].second] --;
		tail--;
		
		// 保证双指针的区间都是满足条件的 
		int head = 0, ans = 0;
		while(tail < 2 * n - 1) {
			tail++;
			cnt[stu[tail].second]++;
			if (cnt[stu[tail].second] == 1) now++;
			while(head < tail && (LL)stu[head].first * 100 < (LL)stu[tail].first * p) {
				cnt[stu[head].second]--;
				if (cnt[stu[head].second] == 0) now--;
				head++;
			}
			ans = max(ans, now);
		}
		printf("Case #%d: %d\n", kase++, ans);
	}
    return 0;
}

E.Friendly Group

**题意: ** 给定n个点,要从n个点中选择k个点,增益为:k个点之间的所有连边数目 - 和这个k个点的连边且不属于这个集合的边数目 - k。

题解: 分析可以知道,对于某一个连通块,如果当前连通块是树,那么点数全部选收益最大。如果是一个有环的图,那么全选的收益最大。所以问题就转换为求一张图的点数和边数。对于每一张图,如果不是树且存在环,那么答案累加m1-n1 ,m1为连通块的边数,n1为连通块的点数

代码:

#include <bits/stdc++.h>

using namespace std;

int const N = 1e6 + 10, M = 4e6 + 10;
typedef long long LL;

int n, T, m;
int e[M], ne[M], h[N], idx, st[N], kase = 1, st2[M];
int m1, n1, flg;

void dfs(int u) {
	for (int i = h[u]; ~i; i = ne[i]) {
		int j = e[i];
		if (!st2[i]) {
			m1++;
			st2[i] = st2[i ^ 1] = 1;
		}
		if (!st[j]) {
			n1++;
			st[j] = 1;
			dfs(j);
		}
	}
	return ;
}
	
void add(int a, int b) {
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int main() {
    cin >> T;
    while(T--) {
    	scanf("%d%d", &n, &m);
    	idx = 0;
    	for (int i = 1; i <= n; ++i) st[i] = 0, h[i] = -1;
    	for (int i = 0; i < 2 * m; ++i) st2[i] = 0;
    	for (int i = 1; i <= m; ++i) {
    		int a, b;
    		scanf("%d%d", &a, &b);
    		add(b, a), add(a, b);
		} 
		int res = 0;
		for (int i = 1; i <= n; ++i) {
			if (!st[i]) {
				m1 = n1 = 0;
				dfs(i);
				res += max(0, m1 - n1);
			}
		} 
		printf("Case #%d: %d\n", kase++, res);
	}
    return 0;
}

F.Good Number

题意: 给定n和k,在1~n的范围内找到x,使得 ⌊ x 1 / k ⌋ ∣ x \lfloor x^{1/k}\rfloor | x x1/kx。求x的个数。 1 < = n 、 k < = 1 0 9 1<=n、k<=10^9 1<=nk<=109

题解: 由于 2 30 2^{30} 230就已经1e9了,因此k的最大值为30,大于30,那么$\lfloor x^{1/k}\rfloor 就 为 1 , 答 案 数 目 为 n 。 如 果 k 为 1 , 那 么 就为1,答案数目为n。如果k为1,那么 1nk1\lfloor x^{1/k}\rfloor $ = x,因此答案数目也为n。对于2~30的k,我们可以知道对于一段$x \in [ l , r ] , 它 的 [l, r],它的 [l,r]\lfloor x^{1/k}\rfloor $值都相同。所以将1 ~ n的区间划分为:$1^k \sim 2^k - 1、2^k \sim 3^k - 1, …, 。 这 样 的 区 间 最 多 有 1 e 5 个 , 且 第 一 个 区 间 内 。这样的区间最多有1e5个,且第一个区间内 1e5\lfloor x^{1/k}\rfloor$的值为1,第二个区间为2,…,第t个区间为t。那么只需要考虑每个区间内有多少个t的倍数即可。而[l, r]范围内t的个数为r/t-l/t+(l%t==0)。

代码:

#include <bits/stdc++.h>

using namespace std;

int const N = 1e3 + 10;
typedef long long LL;

int n, T, k, kase = 1;

LL qmi(int a, int k) {
	LL res = 1;
	while(k) {
		if (k & 1) res = res * a;
		k >>= 1;
		a =  (LL)a * a;
	}
	return res;
}

int main() {
	cin >> T;
	while(T--) {
		cin >> n >> k;
		if ( k == 1 || k > 30) {
			printf("Case #%d: %d\n", kase++, n);
			continue;
		}
		int l = 1, r = 1, cnt = 1;
		LL res = 0;
		while(1) {
			if (r >= n || l >= n) break;
			l = qmi(cnt, k);
			r = min(qmi(cnt + 1, k) - 1, (LL)n);
//			cout << l << " " << r << endl;
			res += (r / cnt) - (l / cnt);	
			if (l % cnt == 0) res++;
			cnt++;
		}
		printf("Case #%d: %lld\n", kase++, res);
	}
    return 0;
}

/*
2
233 1
233 2
*/

J.Kingdom’s Power

题意:

题解:

代码:

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页