Educational Codeforces Round 20

本文精选了五道算法竞赛题目并提供了详细的解题思路及代码实现,包括构造图论问题、最短路径优化、数学问题求解、字符串匹配以及组合计数等,适合算法初学者及竞赛选手参考。

A

按题意来即可

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i < n; ++i)
const int qq = 1e3 + 10;
int gra[qq][qq];

int main(){
	int n, k;	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j){
			if(gra[i][j] == 1)	continue;
			if(i == j){
				if(k >= 1)
					gra[i][j] = 1, k--;
			}else{
				if(k >= 2)
					gra[i][j] = gra[j][i] = 1, k -= 2;
			}
		}
	}
	if(k){
		puts("-1");
		return 0;
	}
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= n; ++j)
			printf("%d ", gra[i][j]);
		puts("");
	}
	return 0;
}


B

每个非0距离0的最短距离一定是它左边的第一个0或者它右边的第一个0,计算这两个位置的0即可

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i < n; ++i)
const int qq = 2e5 + 10;
int num[qq], dist[qq];

int main(){
	int n;	scanf("%d", &n);
	for(int i = 1; i <= n; ++i){
		scanf("%d", num + i);
	}
	int pos = -1;
	for(int i = 1; i <= n; ++i){
		if(num[i] == 0){
			if(pos == -1){
				for(int j = i - 1; j >= 1; --j)
					dist[j] = i - j;
				pos = i;
			}else{
				for(int j = i - 1; j > pos; --j)
					dist[j] = min(dist[j], i - j);
				pos = i;
			}
			continue;
		}
		if(pos == -1)	continue;
		dist[i] = i - pos;
	}
	for(int i = 1; i <= n; ++i)
		printf("%d ", dist[i]);
	puts("");
	return 0;
}


C

题意:给出一个n,一个k,要求在1到n中选k个数,使得和为n,并且这k个数的gcd要最大。

思路:假设选出来的数  a1 + a2 + ... + ak = n, 那么令 g = gcd(a1, a2, ... , ak), 则 b1 + b2 + ... + bk = n/g, 很显然这个g是n的因子,那么我们枚举n的因子判断可行性即可,注意这里n的最大值是1e10, 那么当k大于1e6的时候其实就是不可能的,因为算最小的情况 从1加到1e6是大于1e10的,并且k > n的时候也不可能,排除这两个情况就可以直接做了。

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 1e5 + 10;

int main(){
	LL n, k;
	scanf("%lld%lld", &n, &k);
	if(k >= 1e6 || k > n){
		puts("-1");
		return 0;
	}
	LL f = (1 + k) * k / 2;
	LL ans = 0;
	for(LL i = 1; i * i <= n; ++i){
		if(n % i == 0){
			if(i >= f){
				ans = n / i;
				break;
			}else if(n / i >= f){
				ans = i;
			}
		}
	}
	if(!ans){
		puts("-1");
		return 0;
	}
	LL sum = 0;
	for(int i = 1; i < k; ++i)
		printf("%lld ", i * ans), sum += i * ans;
	printf("%lld\n", n - sum);
	return 0;
}




E

题意:给出一个n一个k,然后给出一个长度为n的字符串,字符只有四种,L,W,D,?,分别代表输,赢,平,任意,现在要求在最后一个回合的时要求输赢数的绝对值等于k,并且前面任意一个回合结束以后输赢的绝对值不能等于k,现在问你把?填完,求一种符合要求的解。

思路:dp[i][j]代表第i回合,赢 - 输数为j的字符串是否存在,因为有负数,所以可以把整体向右平移一个n,然后就状态转移就好。

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define pill pair<int, int>
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i <= n; ++i)
const int qq = 2e3 + 10;
int dp[qq][qq], pre[qq][qq];
char st[qq];
int n, k;

int main(){
	scanf("%d%d", &n, &k);
	scanf("%s", st + 1);
	if(k > n){
		puts("NO");
		return 0;
	}
	dp[0][n] = 1;
	for(int i = 1; i <= n; ++i){
		for(int j = 0; j <= n << 1; ++j){
			if((j == n - k or j == n + k) and i != n)	continue;
			if(st[i] == 'W' and j - 1 >= 0 and dp[i - 1][j - 1])	dp[i][j] = 1, pre[i][j] = -1;
			if(st[i] == 'D' and dp[i - 1][j])		dp[i][j] = 1, pre[i][j] = 0;
			if(st[i] == 'L' and j + 1 <= (n << 1) and dp[i - 1][j + 1])	dp[i][j] = 1, pre[i][j] = 1;
			if(st[i] == '?'){
				if(dp[i - 1][j - 1] and j - 1 >= 0)	dp[i][j] = 1, pre[i][j] = -1;
				if(dp[i - 1][j])		dp[i][j] = 1, pre[i][j] = 0;
				if(dp[i - 1][j + 1] and j + 1 <= (n << 1))	dp[i][j] = 1, pre[i][j] = 1;
			}
		}
	}
//	printf("%d %d\n", dp[n][n + k], dp[n][n - k]);
	if(!dp[n][n - k] and !dp[n][n + k]){
		puts("NO");
		return 0;
	}
	int a = n, b;
	if(dp[n][n + k])	b = n + k;
	else	b = n - k;
	while(a > 0){
		if(pre[a][b] == -1)	st[a] = 'W';
		else if(pre[a][b] == 1)	st[a] = 'L';
		else	st[a] = 'D';
		b = b + pre[a][b];
		a = a - 1;
	}
	printf("%s\n", st + 1);
	return 0;
}



F

题意:给出n个数,要求求出gcd = 1的子序列的数量

思路:我们可以处理出gcd = i * k (k = 1, 2, 3 ......) 的个数,如果我们要求出gcd = i的数量 就减去gcd等于i的倍数即可得到

#include <bits/stdc++.h>

using namespace std;
#define LL long long
#define pb push_back
#define mst(a, b)	memset(a, b, sizeof a)
#define REP(i, x, n)	for(int i = x; i < n; ++i)
const int qq = 1e5 + 10;
const int MOD = 1e9 + 7;
LL ans[qq], p[qq];
LL num[qq];

int main(){
	int n;	scanf("%d", &n);
	p[0] = 1;
	for(int i = 1; i <= n; ++i)
		p[i] = (p[i - 1] * 2LL) % MOD;
	int maxn = 0;
	for(int i = 1; i <= n; ++i){
		int x;	scanf("%d", &x);
		maxn = max(maxn, x);
		num[x]++;
	}
	for(int i = 1; i<= maxn; ++i){
		for(int j = i + i; j <= maxn; j += i)	num[i] += num[j];
	}
	for(int i = maxn; i > 0; --i){
		ans[i] = p[num[i]] - 1;
		for(int j = i + i; j <= maxn; j += i)	ans[i] = (ans[i] - ans[j] + MOD) % MOD;
	}
	printf("%lld\n", ans[1]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值