2020 China Collegiate Programming Contest Qinhuangdao Site

第25小時發表於2020-10-24

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

題意:

題解:

程式碼:

相關文章