2020牛客寒假算法基础集训营第二场


2020牛客寒假算法基础集训营第二场


前言


比赛AC


A.做游戏
简明题意
  • 牛牛和 牛可乐进行了多轮游戏, 牛牛总共出了 A 次石头,B 次剪刀,C 次布;牛可乐总共出了 X 次石头,Y 次剪刀,Z 次布。 你需要求出 牛牛最多获胜多少局。
正文
  • 就假设牛牛尽可能赢,那么每次取min就可以了。我开始wa了,因为觉得这样贪心是错的。。。后来回过来发现,是溢出了,因为可以取次1e9,即使把答案设成了long long,但是运算过程中炸了。。。。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

void solve()
{
	int a, b, c, x, y, z;
	cin >> a >> b >> c >> x >> y >> z;

	long long ans = (long long)min(a, y) + min(b, z) + min(c, x);

	cout << ans;
}

int main()
{
	solve();
	return 0;
}

B.排数字
简明题意
  • 给定数字字符串,你可以任意交换位置,问最多形成多少个616字串。61616算两个字串。
正文
  • 只用考虑61了,算一下16的数量,理解为6后面16循环,假设全是16,所以答案是min(cnt_6,cnt),但现在要给最开头加一个6,所以答案是min(cnt_6-1,cnt_1)
代码
 #include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
 
void solve()
{
    int n;
    scanf("%d", &n);
 
    int a1 = 0, a6 = 0;
    for (int i = 1; i <= n; i++)
    {
        int t;
        scanf("%1d", &t);
        if (t == 1)
            a1++;
        if (t == 6)
            a6++;
    }
     
    int ans = 0;
 
    if (a6 > a1)
        ans = a1;
    else if (a1 >= a6)
        ans = a6 - 1;
 
     
    cout << ans;
}
 
int main()
{
    solve();
    return 0;
}

D.数三角
简明题意
  • 给定n个点(n<=500),需要你求出这n个点能组成多少个钝角三角形
正文
  • 一开始直接百度去了,HDU有道类似的题,代码拿过来改改发现改不对。后来寻思这数据也不大,就暴力 n 3 n^3 n3试试了,就直接枚举任意三个点,这三点能形成钝角三角形的充要条件就是任意一个角是钝角,所以枚举三个点后,可以直接算出三边长,然后余弦定理可以直接判断是否为钝角。
  • 但是这里我T了好多次,我太实诚了,直接把余弦定理的 cos ⁡ θ = a 2 + b 2 − c 2 2 a b \cos\theta=\frac{a^2+b^2-c^2}{2ab} cosθ=2aba2+b2c2算出来了。。。你想啊,钝角,一定满足 cos ⁡ θ < 0 \cos\theta<0 cosθ<0,也就是 a 2 + b 2 − c 2 2 a b < 0 \frac{a^2+b^2-c^2}{2ab}<0 2aba2+b2c2<0,也就是 a 2 + b 2 < c 2 a^2+b^2<c^2 a2+b2<c2。所以连开根号都不需要了。以后一定要记得,判断三角形是直角/锐角/钝角,直接用三边关系判断就可以了。
  • 但其实这里还有个漏洞。判断三角形用三边关系确实是充要条件,但前提是它是一个三角形。所以需要特判一下,就是这个原因我WA了30次,不知道为啥。。。自闭了。。。
  • 这里提下下怎么判断,有两种方法,一种是用斜率,一种是用面积。斜率就不必说了,这里说一下面积。 S = 1 2 ( x 1 y 2 + x 2 y 3 + x 3 y 1 − x 2 y 1 − x 3 y 2 − x 1 y 3 ) S=\frac{1}{2}( x_1y_2+x_2y_3+x_3y_1-x_2y_1-x_3y_2-x_1y_3) S=21(x1y2+x2y3+x3y1x2y1x3y2x1y3)(自行加绝对值)
  • 另外这题没有说坐标是整型还是浮点型,所以我用了long double
代码
#pragma GCC optimize(2)
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

long double x[600], y[600];

void solve()
{
	long long n;
	cin >> n;
	for (long long i = 1; i <= n; i++)
		cin >> x[i] >> y[i];

	long long ans = 0;
	for (long long i = 1; i <= n; i++)
		for (long long j = i + 1; j <= n; j++)
			for (long long k = j + 1; k <= n; k++)
			{
				long double a = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
				long double b = (x[i] - x[k]) * (x[i] - x[k]) + (y[i] - y[k]) * (y[i] - y[k]);
				long double c = (x[k] - x[j]) * (x[k] - x[j]) + (y[k] - y[j]) * (y[k] - y[j]);

				if ((x[i] * y[j] - x[j] * y[i]) + (x[j] * y[k] - x[k] * y[j]) + (x[k] * y[i] - y[k] * x[i]) == 0) continue;

				if ((b + c < a) || (b + a < c) || (a + c < b)) ans++;
			}
	cout << ans;
}

int main()
{
	solve();
	return 0;
}

E.做计数
简明题意
  • 给出n,要满足 i ∗ j < = n i*j<=n ij<=n。现在问你满足 i + j = k \sqrt{i}+\sqrt{j}=\sqrt{k} i +j =k 的三元组 ( i , j , k ) (i,j,k) (i,j,k)有多少对。
正文
  • i ∗ j < = n i*j<=n ij<=n很容易想到枚举n的约数,这样枚举的复杂度只是 n \sqrt{n} n 。每次枚举后得到了ij,现在问题就是判断 i \sqrt{i} i j \sqrt{j} j 能否合并了。
  • 下一步,对于一个n我们还需要枚举[1,n]中所有的数,然后每一个数做一次上面的步骤,而且枚举了约束,还要判断是否能合并。。。
  • 我们把原式左右同时平方,得到 i + 2 i j + j = k i+2\sqrt{ij}+j=k i+2ij +j=k,那么只要 i j ij ij是完全平方数,那么就一定可以合并。所以就只需要在第一层中枚举所有的完全平放数,第二层里计算一下约数个数就可以了。约数个数其实是可以线筛的,这里暴力就可以。
代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

int f(int n)
{
	int ans = 0;
	for (int i = 1; i * i <= n; i++)
	{
		if (i * i == n)
		{
			ans++;
			break;
		}
		int a = i, b = n / i;
		if (n % a != 0)
			continue;
		ans += 2;
	}
	return ans;
}

void solve()
{
	int n;
	scanf("%d", &n);

	long long ans = 0;
	for (int i = 1; i <= n; i++)
	{
		if (i * i > n)
			break;
		ans += f(i * i);
	}

	cout << ans;
}

int main()
{
	solve();
	return 0;
}

G.判正误
简明题意
  • 给定abcdefg,让你验证 a d + b e + c f = g a^d+b^e+c^f=g ad+be+cf=g。数据都在1e9
正文
  • 官方题解里说,直接计算会 TLE / MLE ,考虑在模意义下进行计算,若 a d + b e + c f ≡ g ( m o d M ) a^d+b^e+c^f\equiv g \pmod M ad+be+cfg(modM),则原式有概率成立,多选择一些模数以提高正确率。这里具体讲一下,什么时候是对的什么时候不对。
  • a d + b e + c f ≡ g ( m o d M ) a^d+b^e+c^f\equiv g \pmod M ad+be+cfg(modM)可以得 a d + b e + c f + k M = g a^d+b^e+c^f+kM=g ad+be+cf+kM=g,也就是 a d + b e + c f − g = k M a^d+b^e+c^f-g=kM ad+be+cfg=kM时,会有误判。但样例就那么多,所以不断改变M的值,就能AC了。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;

const long long mod = 1e9 + 9;

long long ksm(long long a, long long b)
{
	if (a == 0) return 0;
	long long res = 1;
	while (b )
	{

		if (b & 1)
			res = res * a%mod;
		b = b >> 1;
		a = a * a %mod;
	}
	return res;
}


void solve()
{
	//cout << ksm(-2, 3) << endl;
	int t;
	cin >> t;
	while (t--)
	{
		long long a, b, c, d, e, f, g;
		cin >> a >> b >> c >> d >> e >> f >> g;

		long long x = ksm(a, d);
		long long y = ksm(b, e);
		long long z = ksm(c, f);

		long long ans = (x + y + z + mod) % mod;

		printf("%s\n", ans == (g + mod) % mod ? "Yes" : "No");
	}
}

int main()
{
	solve();
	return 0;
}

赛后补题

C.算概率
简明题意
  • 有n道题,每道题答对的概率已经给出。需要依次求出恰好答对0,1,2 ··· n道题的概率(n<=2000)
正文
  • 比赛的时候完全没有头绪,第一个障碍是分数取模,后来官方给了通知说模意义下的分数你不用管,直接把它当正常的算就可以了。然而我知道了也毫无头绪,我当时只能想到暴力算。
  • 正解是dp,没学过概率dp。。。看了题解发现还好能理解。其实也蛮简单, d p [ i ] [ j ] dp[i][j] dp[i][j]表示前i题做对j题的概率,那么转移的时候,只需要枚举第i题对与不对,对,那么就考虑前i-1题对j-1道,不对,就考虑前i-1题对j道。
代码
#include<cstdio>
#include<iostream>
using namespace std;

const int mod = 1e9 + 7;

int p[2000 + 10], dp[2000 + 10][2000 + 10];

void solve()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> p[i];

	dp[0][0] = 1;
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= i; j++)
			dp[i][j] = (1ll * dp[i - 1][j] * (1 - p[i]) % mod  + (j-1>=0?1ll*dp[i - 1][j - 1] * p[i]%mod:0) + mod)%mod;

	for (int i = 0; i <= n; i++)
		if (i != n) printf("%d ", dp[n][i]);
		else printf("%d", dp[n][n]);
}

int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

简明题意
正文
代码

简明题意
正文
代码

简明题意
正文
代码

简明题意
正文
代码

总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值