AtCoder Beginner Contest 173 (C——F)

C - H and V

给一个n x m的矩阵,由’#‘和’.'构成,分别代表黑色和白色,现在你可以任选若干行和若干列将其涂成红色,问有多少种涂法使最终只有k个黑色。

#include
#include
#include
#include
using namespace std;
char a[10][10];
long long int n, m, k;
long long num=0, ans=0;
int used[10] = { 0 };
void dfs(int r, int c, int sum)//行 列 涂去的黑色
{
	int add = 0;
	if (r == n && c == m)
	{
		if (num - sum == k)
			ans++;
		return;
	}
	if (r == n)
	{
		for (int i = 0; i < n; i++)
		{
			if (a[i][c] == '#' && (!used[i]))
			{
				add++;
			}
		}
		dfs(r, c + 1, sum + add);
		dfs(r, c + 1, sum);
	}
	else
	{
		for (int i = 0; i < m; i++)
		{
			if (a[r][i] == '#')
			{
				add++;
			}
		}
		used[r] = 1;
		dfs(r + 1, c, sum + add);
		used[r] = 0;
		dfs(r + 1, c, sum);
	}
	
}
int main()
{
	cin >> n >> m >> k;
	for (int i = 0; i < n; i++)
		cin >> a[i];
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (a[i][j] == '#')
				num++;
		}
	}
	dfs(0, 0, 0);
	cout << ans << endl;
}

D - Chat in a Circle

现在有一排数字,现在要将这排数字按顺序放入一个圈,每个数字在插入圈时,他的权值就是相邻两个数字中小的那个,第一个插入的人权值为0,问总权值是多少。

既然要让权值最大,那必然要让大的值先插入,否则会被相邻的小的值影响,导致不能贡献权值。所以要先排序。又因为是一个圈,左右两边都能贡献权值,所以除了最大的数只能贡献一次,剩余的数都可以贡献两次。注意判断数量的奇偶。

#include
#include
#include
#include
using namespace std;
long long int  m,t;
long long int a[200005];
int main()
{
	cin >> t;
	for (int i = 1; i <= t; i++)
	{
		cin >> a[i];
	}
	sort(a + 1, a + 1 + t);
	m = a[t];
	int j = t - 1;
	for (int i = (t - 2) / 2; i > 0; i--)
	{
		m += 2 * a[j--];
	}
	if (t % 2 == 1)
	{
		m += a[j];
	}
	cout << m;
}

E - Multiplication 4

求从n个数选出k个数的乘积最大值结果再取模1e9+7。

对于多个数的乘积想要结果最大,在满足绝对值最大的情况下要满足包含偶数个负数。
对于所选的数是奇数时,首先就取一个最大正数,接下来就是俩个正数积和俩个负数积取最大。
当正数数量为0,且选择数量为奇数时结果一定为负,这时要想结果最大的话就要取k个绝对值最小的数。

#include
#include
#include
#include
using namespace std;
long long int mod = 1e9 + 7;
long long int  n,m;
long long int a[200005];
bool cmp(int x, int y)
{
	return abs(x) < abs(y);
}
int main()
{
	int pn=0 ,nn = 0;
	//pn正数,nn负数;
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		if (a[i] >= 0)
			pn++;
		else
			nn++;
	}
	int l=1, r=n;//左右逼近
	long long ans = 1;
	if (!pn && m % 2 == 1)//结果必定为负数
	{
		sort(a + 1, a + 1 + n, cmp);
		for (int i = 1; i <= m; i++) 
			ans = a[i] * ans % mod;
	}
	else
	{
		sort(a + 1, a + 1 + n, greater< int >());
			内置类型的由大到小排序
		while (m)
		{
			if (m % 2 == 1)//为奇数就乘大的正数
			{
				ans = ans * a[l] % mod;
				l++, m--;
			}
			else
			{
				if (a[l] * a[l + 1] >= a[r] * a[r - 1])
					//两正数乘起来比负数大
				{
					ans = ans * a[l] % mod * a[l + 1] % mod;
					l += 2,m -= 2;
				}
				else
				{
					ans = ans * a[r - 1] % mod * a[r] % mod;
					r -= 2,m -= 2;
				}
			}

		}
	}
	//防止出现负数,故再加mod再取余数
	cout << (ans % mod + mod) % mod << endl;
}

F - Intervals on Tree

给你一棵树,节点为1到n,取若干连续编号的节点,求出其连通分量,问所有连续段的连通分量之和。

直接算不好算所以先算一开始一条边都没有的时候答案是多少,然后每加入一个边,就把含他的s的数量去掉。

#include
#include
#include
#include
using namespace std;
int main()
{
	long long n, l, r,sum=0;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		sum += i * (n - i + 1);
	}
	for (int i = 1; i < n; i++)
	{
		cin >> l >> r;
		if (l > r)
			swap(l, r);
		sum -= l * (n - r + 1);
	}
	cout << sum << endl;
}

你可能感兴趣的:(补题记录)