牛客国庆集训派对Day2(A F H)

25 篇文章 0 订阅
4 篇文章 0 订阅

链接: https://www.nowcoder.com/acm/contest/202#question

A. 矩阵乘法(分块)

题意: 给两个矩阵 A , B A, B A,B A A A 是十进制矩阵, B B B 是二进制矩阵,问两个矩阵相乘之后得到的结果矩阵的每个元素的异或和。
思路: 因为 B B B 是二进制矩阵, B B B 矩阵最多只有 64 行,分块后,它的组合情况其实很少。 若对 A A A 分块, 每行每 8 个分一块,那 B B B 中就只有 256 种情况。 对矩阵 A A A 每行最多 8 个块的 256 种情况预处理,预处理时采用类似状压dp的方式,每个预处理的数只需要在之前的基础上加上二进制最后一位 1 对应的没被处理过的数就行了。(注意二进制全是0的时候一定是0不需要考虑)相乘的结果就是每行中的每块值对应查表,在加起来就是了。复杂度O(8 * n *m)。
(比赛时不会,看了题解,又看了别人代码才写出来。后来听说暴力也能过,我只能给牛客的评测机点个赞了。)

#include <bits/stdc++.h>
#define lowbit(x) x&(-x)
using namespace std;
const int MAXN = 5000;
const int MAXP = 100;

int f[MAXN][10][300];
int n, p, m; 
int A[MAXN][MAXP]; 
int C[MAXN][MAXN];
unsigned long long B[MAXN];
int num[300];

int main(int argc, char const *argv[])
{
	scanf("%d%d%d", &n, &p, &m);
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < p; ++j) {
			scanf("%x", &A[i][j]);
		}
	}
	char c[100];
	for (int i = 0; i < m; ++i) {
		scanf("%s", c);
		for (int j = p-1; j >= 0; --j) {
			B[i] <<= 1;//|(c[j]^'0');
			B[i] += (c[j]^'0');
		}// cout<<B[i]<<endl;
	}

	for (int i = 1; i < 8; ++i) {
		num[1<<i] = i;//cout<<"num : "<<num[1<<i]<<endl;
	}
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < 8; ++j) {
			for (int k = 1; k < 256; ++k) {// k == 0 的时候就是0, 从1开始
				f[i][j][k] = f[i][j][k^lowbit(k)] + A[i][j*8+num[lowbit(k)]];
				// cout<<f[i][j][k]<<" ";
			}// cout<<endl;
		}// cout<<endl<<endl;
	}
	// cout<<"sajdjas"<<endl;
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < m; ++j) {
			for (int k = 0; k < 8; ++k) {
				C[i][j] += f[i][k][B[j]>>(k*8)&255];				
			}
		}
	}
	int ans = 0;
	for (int i = 0; i < n; ++i) {
		for (int j = 0; j < m; ++j) {
			ans ^= C[i][j];
			// cout<<C[i][j]<<" ";
		}//cout<<endl;
	}
	cout<<ans<<endl;
	return 0;
}

F. 平衡二叉树(数据结构)

题意: 满足左右子树高度差不超过 d d d 的二叉树称为平衡二叉树。不平衡度是树中所有节点的左右子树的节点数之差的最大值。给定 d , n d, n d,n ,求不平衡度的最大值。
思路:
1.当 d &gt; = n − 1 d &gt;= n-1 d>=n1 时,可以假定根节点的左子树是满二叉树,右子树是空树,直接输出 高度 n − 1 n-1 n1 的满二叉树的节点个数 2 n − 1 − 1 2^{n-1} -1 2n11
2.当 d &lt; n − 1 d &lt; n-1 d<n1 时, 仿照之前的思路,假定根节点的左子树是满二叉树,右子树是符合平衡要求的节点最少的平衡二叉树。还记得最小平衡二叉树节点数公式吗
f ( h ) = { h   h ≤ d   f ( h − 1 ) + f ( h − 1 − d ) + 1   h &gt; d   f(h)= \begin{cases} h &amp; \text { $h \leq d$ } \\ f(h-1) + f(h-1-d) + 1 &amp; \text{ $h &gt; d$ } \end{cases} f(h)={hf(h1)+f(h1d)+1 hd  h>d 
左子树的高度是 n − 1 n-1 n1 , 所以右子树的高度只需要 n − 1 − d n-1-d n1d 就可以了,所以答案即 2 n − 1 − 1 − f ( n − 1 − d ) 2^{n-1} - 1 - f(n-1-d) 2n11f(n1d)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n, d;

LL power(LL x, LL n) {
	LL res = 1;
	while(n) {
		if(n & 1) {
			res = (res*x);
		}
		x = x*x;
		n >>= 1;
	} 
	return res;
}

int main(int argc, char const *argv[])
{
	cin>>n>>d;
	LL f[100];
	if(n == 0) {
		cout<<0<<endl;
		return 0;
	}
	if(d >= n-1) {
		cout<<power(2, n-1)-1<<endl;
		return 0;
	}
	for (LL i = 0; i <= d; ++i) {
		f[i] = i;
	}
	for (LL i = d+1; i <= n-1-d; ++i) {
		f[i] = f[i-1] + f[i-1-d] + 1;
	}
	LL ans = power(2, n-1)-1 - f[n-1-d];
	cout<<ans<<endl;
	return 0;
}

H. 卡牌游戏(数论-期望)

题意 : N N N 种牌, M M M 种稀有,每抽一次,会随机从 N N N 种牌选择一个,但 M M M 种牌不会重复抽到, 现在想得到 K K K 种稀有卡牌,问抽牌的次数的期望是多少。
思路:

  1. 先考虑 K = = 1 K == 1 K==1 的情况,
    第一次抽到的概率是 M N \frac M N NM
    第二次抽到的概率是 N − M N ∗ M N \frac {N-M} N * \frac M N NNMNM

    n n n 次抽到的概率是 ( N − M N ) n − 1 ∗ M N (\frac {N-M} N )^{n-1}* \frac M N (NNM)n1NM
    期望 E = 1 ∗ M N + 2 ∗ N − M N ∗ M N + . . . + n ∗ ( N − M N ) n − 1 ∗ M N E = 1 * \frac M N + 2 * \frac {N-M} N * \frac M N + ... + n * (\frac {N- M} N )^{n-1}* \frac M N E=1NM+2NNMNM+...+n(NNM)n1NM
    E = 1 − ( N − M N ) n 1 − N − M N − n ∗ ( N − M N ) n E = \frac {1 - (\frac {N-M} N)^n} {1 - \frac {N-M} N} - n * (\frac {N-M} N)^n E=1NNM1(NNM)nn(NNM)n
    n n n 趋于无穷时 E = N M E = \frac N M E=MN
  2. K = = 2 K == 2 K==2 时, 因为 M M M 种稀有牌不会重复得到, 所以可以分解为两个子问题, 即可以理解为先从 N N N 种牌, M M M 种稀有中得到 1 1 1 种, 再从 N − 1 N-1 N1种牌, M − 1 M-1 M1 种稀有中得到 1 1 1 种。所以 E = N M + N − 1 M − 1 E = \frac N M + \frac {N-1} {M-1} E=MN+M1N1
  3. 以此类推, 当 K = = K K == K K==K 时, E = ∑ i = 0 K − 1 N − i M − i E = \sum ^{K-1} _{i=0}\frac {N-i} {M-i} E=i=0K1MiNi
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int t, cas = 0;
    double n, m, k;
    cin>>t;
    while(t--) {
        cin>>n>>m>>k;
        double ans = 0;
        for(int i = 0; i < k; ++i) {
            ans += (n-i)/(m-i);
        }
        cout<<"Case #"<<++cas<<": "<<fixed<<setprecision(12)<<ans<<endl;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值