解题报告-2019国庆清北Day5

这篇博客详细记录了2019年国庆清北算法竞赛第五天的解题报告,包括了三道题目——最小差异矩阵、分割序列和树的魔法值。解题思路涉及二分查找、动态规划和满二叉树的计算。对于最小差异矩阵问题,采用排序和贪心策略;分割序列问题通过动态规划求解;树的魔法值问题则是计算预期值的问题。
摘要由CSDN通过智能技术生成

Day5

下载题面请戳这里

最小差异矩阵(a.cpp, a.in, a.out)

题面

【题目描述】

有一个 n*m 的矩阵,矩阵的每个位置上可以放置一个数。对于第 i 行,第 i 行的差异定

义为该行的最大数和最小数的差。一个矩阵的差异,定义为矩阵中每一行差异的最大值。现

在给定 k 个数 v[1…k],问:从这 k 个数中选 n*m 个数放入矩阵,能够得到的矩阵的差异最

小值是多少。

【输入格式】

第一行三个整数,k, n, m,表示有 k 个数可选,矩阵的行数和列数分别为 n 和 m。

第二行 k 个整数,表示备选的数 v[1…k]。

【输出格式】

输出一个数,表示能够得到的最小差异值

【样例输入】

5 2 2

7 5 8 2 3

【样例输出】

1

【数据范围】

对于 30%的数据,k<= 10, n <= 3, m <= 3

对于 100%的数据,n * m <= k <= 100000, n, m <= 1000,0<= v[i] <= 10^9

【时空限制】

256MB,1s

考场思路

仔细读题,就发现这道题目是要让最大值最小,于是貌似就可以去二分答案。可以发现,如果把所有可以填的数排一下序,那么最优方法中每一行所选的数在排好序的序列中一定是连续的,所以可以贪心去取每一行的数,来验证答案。

考场代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define INF 0x7fffffff
#define re register
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define qwq printf("qwq\n");

using namespace std;

int read()
{
	register int x = 0,f = 1;register char ch;
	ch = getchar();
	while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
	while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
	return x * f;
}

int n,m,k,l,r,mid,ans,a[100005];

int cmp(int x,int y){return x < y;}

int check(int v)
{
	int p1 = 0,p2 = 0,cnt = 1;
	while(cnt <= n)
	{
		p1 = p2 = p2 + 1;
		while(p2 - p1 + 1 < m)
		{
			p2++;
			if(p2 > k) return 0;
			while(a[p2] - a[p1] > v) p1++;
		}
		cnt++;
	}
	return 1;
}

int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	k = read(); n = read(); m = read();
	for(int i = 1; i <= k; i++) a[i] = read(),r = max(r,a[i]);
	sort(a + 1, a + k + 1,cmp);	
	while(l < r)
	{
		mid = (l + r) >> 1
		if(check(mid)) r = mid,ans = mid;
		else l = mid + 1;
	}
	printf("%d\n",ans);
	fclose(stdin); fclose(stdout);
    return 0;
}
题解

在这里插入图片描述

std
#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 7;
int n, m, k, v[maxn];

int judge(int d)
{
	int tmp = 0;
	for (int i=1; i+m-1<=k; ++i)
	{
		if (v[i+m-1] - v[i] <=d)
			++tmp, i += m - 1;
	}
	if (tmp >= n) return 1;
	return 0;
}

int main()
{
	freopen("a.in", "r", stdin);
	freopen("a.out", "w", stdout);
	scanf("%d%d%d", &k, &n, &m);
	for (int i=1; i<=k; ++i)
		scanf("%d", &v[i]);
	sort(v + 1, v + k + 1);
	int left = 0, right = 1e9;
	while (left < right)
	{
		int mid = (left + right) / 2;
		if (judge(mid))
			right = mid;
		else
			left = mid + 1;
	}
	printf("%d\n", left);
	return 0;
}

分割序列(b.cpp, b.in, b.out)

【题目描述】

给定一个长度为 n 的序列 v[1…n],现在要将这个序列分成 k 段(每段都丌能为空),定

义每一段的权值为该段上的所有数的戒和。定义整个序列的权值为每段权值的和。问:这个

序列的最大权值为多少。

【输入格式】

第一行两个数 n 和 k,意义如题意所示。

第二行 n 个数,表示这个序列 v[1…n]。

【输出格式】

输出一个数,代表这个序列的最大权值。

【输入样例】

5 2

7 5 8 2 3

【输出样例】

22

【数据范围】

对于 30%的数据,n<= 10, k <= 10

对于 60%的数据,n <= 100, k <= 100

对于 100%的数据,k <= n <= 2000,1<= v[i] <= 5 * 10^5

【时空限制】

256MB,1s

考场思路

一看到题就感觉像是个DP,于是就敲了一个 O ( n 3 ) O(n^3) O(n3)的暴力。

状态设计:用 f [ i ] [ j ] f[i][j] f[i][j]表示把区间1~i分成j段能获得的最大价值。
转移:$f[i][j]=\max(f[k][j-1]+l[k+1][i]) , 其 中 ,其中 l[i][j]$表示对区间[i,j]按位或的结果。

考场代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define INF 0x7fffffff
#define re register
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define qwq printf("qwq\n");

using namespace std;

int read()
{
	register int x = 0,f = 1;register char ch;
	ch = getchar();
	while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
	while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
	return x * f;
}

long long n,k,v[100005],l[2005][2005],f[2005][2005];

int main()
{
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	n = read(); k = read();
	for(int i = 1; i <= n; i++) l[i][i] = read();
	for(int i = 1; i <= n; i++)
		for(int j = i + 1; j <= n; j++)
			l[i][j] = l[i][j - 1] | l[j][j];
	for(int i = 1; i <= n; i++) f[i][1] = l[1][i];
	for(int i = 1; i <= n; i++)
		for(int j = 2; j <= k; j++)
		{
			if(j > i) break;
			for(int m = 1; m < i; m++)
				f[i][j] = max(f[i][j],f[m][j - 1] + l[m + 1][i]);
		}
	printf("%lld\n",f[n][k]);
	fclose(stdin); fclose(stdout);
    return 0;
}
题解

在这里插入图片描述

考虑用单调性去优化dp的复杂度。

l [ i ] [ j ] l[i][j] l[i][j]j不变,关于i单调递减

std
#include <bits/stdc++.h>
using namespace std;

const long long inf = 1ull << 30 << 20;
const int maxn = 2005;
vector<int> point[maxn];
int n, K, v[maxn], sum[maxn][maxn];
long long f[maxn][maxn];
int main()
{
	freopen("b.in", "r", stdin);
	freopen("b.out", "w", stdout);
	cin >> n >> K;
	for (int i=1; i<=n; ++i) cin >> v[i];
	for (int i=1; i<=n; ++i) sum[i][i] = v[i];
	for (int i=1; i<n; ++i)
		for (int j=i+1; j<=n; ++j)
			sum[i][j] = sum[i][j-1] | v[j];
	for (int i=1; i<=n; ++i)
	{
		point[i].push_back(i);
		for (int j=i-1; j>=1; --j)
			if (sum[j][i] != sum[j+1][i])
				point[i].push_back(j);
	}
	for (int k=1; k<=K; ++k)
		for (int i=1; i<=n; ++i)
		{
			if (i < k) continue;
			for (int j=0; j<point[i].size(); ++j)
			{
				int x = point[i][j];
				if (x >= k)
					f[i][k] = max(f[i][k], f[x-1][k-1] + sum[x][i]);
			}
		}
	cout << f[n][K] << endl;
	return 0;
}

树的魔法值(C.cpp, C.in, C.out)

【题目描述】

有一棵 k+1 层的满二叉树,那么该树有 2^k 个叶子节点。给定 n 个机器人(n=2^k),

编号从 1—n,编号为 i 的机器人的权值为 v[i]。我们现在要将这 n 个机器人分别放置在这 n

个叶子节点上,每个叶子节点放且只能放一个机器人。叶子节点的权值为该叶子节点上的机

器人的权值。非叶子节点的权值定义为该树中编号最大的机器人的权值。每个非叶子节点除

了权值以外,还有一个魔法值,该点的魔法值为其左右儿子节点的权值的乘积。整棵树的魔

法值定义为非叶子节点的魔法值的和。

问:将这 n 个机器人随机地放在这 n 个叶子节点上,树的魔法值的期望为多少。

【输入格式】

第一行为一个整数 k,含义如题所示。

第二行为 2^k 个整数,依次表示这 n 个机器人的权值。

【输出格式】

假设答案为一个丌可约分数 P/Q,则输出在模 1e9+7 意义下的 P * (Q^-1)模 1e9+7

的值。

【样例输入 1】

2

1 3 5 7

【样例输出 1】

59

【样例解释】

对于 n=4 的情况,机器人共有 24 种丌同的安放方案。其中,本质丌同的有 3 种,分

别 是 ((1,3),(5,7)), ((1,5),(3,7)), ((1,7),(5,3)) , 魔 法 值 分 别 为 13+57+3*7=59,

15+37+57=61, 17+53+57=57, 答案为(57+59+61)/3 = 59。

【样例输入 2】

2

1 5 3 7

【样例输出 2】

333333390

【数据范围】

30%的数据,k <= 3

60%的数据,k<= 10

100%的数据,k<= 18

【时空限制】

256MB,1s

考场思路

到这个题我只有半个小时了,看不懂样例2(老师讲题时才发现读错题了),想打一个暴力然后发现自己忘了怎么算逆元了,然后就gugu了。大概就按照线段树建树的方式瞎搞了一下,貌似还搞错了……

考场代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define INF 0x7fffffff
#define re register
#define mod 1000000007
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define qwq printf("qwq\n");

using namespace std;

int read()
{
	register int x = 0,f = 1;register char ch;
	ch = getchar();
	while(ch > '9' || ch < '0'){if(ch == '-') f = -f;ch = getchar();}
	while(ch <= '9' && ch >= '0'){x = x * 10 + ch - 48;ch = getchar();}
	return x * f;
}

long long n,k,ans,cnt,sum,a[100005],pd[100005],yx[100005];

char s[25][40320],now[25];

long long ksm(long long  x,long long y)
{
    long long ans = 1;
    while(y > 0)
    {
        if(y & 1) ans = (ans * x) % mod;
        x = (x * x) % mod;
        y = y >> 1;
    }
    return ans % mod;
}

int check()
{
	for(int i = 1; i <= cnt; i++) if(strcmp(now,s[i]) == 0) return 0;
	strcpy(s[++cnt],now);
	return 1;
}

long long build(int l,int r)
{
	if(l == r) return a[l];
	int mid = (l + r) >> 1;
	int t1 = build(l,mid);
	int t2 = build(mid + 1,r);
	sum += t1 * t2;
	return max(t1,t2);
}

void tj()
{
	if(check() == 0) return ;
	sum = 0;
	build(1,n);
	ans = ans + sum;
}

void dfs(int k)
{
	if(k > n)
	{
		tj();
		return ;
	}
	for(int i = 1; i <= n; i++)
	{
		if(pd[i]) continue;
		pd[i] = 1;
		now[k - 1] = a[i];
		yx[k] = a[i];
		dfs(k + 1);
		pd[i] = 0;
	}
}

int main()
{
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	k = read();
	n = 1 << k;
	for(int i = 1; i <= n; i++) a[i] = read();
	dfs(1);
	if(ans % cnt == 0) printf("%lld\n",ans / cnt);
	else printf("%lld\n",(ans * ksm(cnt,1000000005)) % (1000000007));
	fclose(stdin); fclose(stdout);
    return 0;
}
题解
  1. 30分:全排列,暴力求期望
  2. 60分:

在这里插入图片描述
在这里插入图片描述

  1. 100分

    听不懂……

std
#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;
const int maxn = (1 << 18) + 7;
typedef long long LL;

LL fac[maxn], inv_fac[maxn], bit[maxn], sum[maxn];
int n, v[maxn], k;
int powmod(int x, int times)
{
	LL tmp = 1;
	while (times > 0)
	{
		if (times & 1) tmp = tmp * x % mod;
		x = (LL)x * x % mod;
		times >>= 1;
	}
	return tmp;
}

LL C(int x, int y)
{
	if (x < y) return 0;
	return fac[x] * inv_fac[y] % mod * inv_fac[x-y] % mod;
}

int main()
{
	freopen("c.in", "r", stdin);
	freopen("c.out", "w", stdout);
	fac[0] = 1; for (int i=1; i<maxn; ++i) fac[i] = fac[i-1] * i % mod;
	inv_fac[maxn-1] = powmod(fac[maxn-1], mod - 2); 
	for (int i=maxn-2; i>=0; --i) inv_fac[i] = inv_fac[i+1] * (i + 1) % mod;
	bit[0] = 1; for (int i=1; i<=20; ++i) bit[i] = bit[i-1] * 2;
	
	scanf("%d", &k);
	n = bit[k];
	for (int i=1; i<=bit[k]; ++i) scanf("%d", &v[i]);
	LL ans = 0;
	for (int d=1; d<=k; ++d)
	{
		LL tmp = 0;
		for (int i=n; i>=bit[d]; --i)
			sum[i] = (sum[i+1] + C(i-1-bit[d-1], bit[d-1]-1) * v[i]) % mod;
		for (int j=n-1; j>=bit[d-1]; --j)
			tmp = (tmp + C(j-1, bit[d-1]-1) * v[j] % mod * sum[max(bit[d], (LL)j + 1)]) % mod;
		ans = (ans + tmp * fac[bit[d-1]] % mod * fac[bit[d-1]] % mod * fac[n-bit[d]] % mod 
				* 2 % mod * bit[k-d]) % mod;
	}
	ans = ans * inv_fac[n] % mod;
	cout << ans << endl;
	return 0;
}
anf("%d", &k);
	n = bit[k];
	for (int i=1; i<=bit[k]; ++i) scanf("%d", &v[i]);
	LL ans = 0;
	for (int d=1; d<=k; ++d)
	{
		LL tmp = 0;
		for (int i=n; i>=bit[d]; --i)
			sum[i] = (sum[i+1] + C(i-1-bit[d-1], bit[d-1]-1) * v[i]) % mod;
		for (int j=n-1; j>=bit[d-1]; --j)
			tmp = (tmp + C(j-1, bit[d-1]-1) * v[j] % mod * sum[max(bit[d], (LL)j + 1)]) % mod;
		ans = (ans + tmp * fac[bit[d-1]] % mod * fac[bit[d-1]] % mod * fac[n-bit[d]] % mod 
				* 2 % mod * bit[k-d]) % mod;
	}
	ans = ans * inv_fac[n] % mod;
	cout << ans << endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 高考2005-2019年的Word真题主要围绕文本编辑、格式调整、插入图表等方面进行考查。下面我将从这几个方面进行回答。 一、文本编辑: Word真题中一般会涉及到文本编辑的操作,如文本的输入、删除、替换、复制、粘贴等。考生需要熟练掌握这些基本的文本编辑技巧,能够准确无误地完成题目要求。 二、格式调整: 在Word真题中,格式调整是一个常见的考点,主要包括字体格式、落格式和页面格式等方面。考生需要熟练掌握字体的修改、字号、字形、颜色等操作,了解落格式的调整,如对齐方式、行距、缩进等,以及页面格式的设置,如页边距、页眉页脚等。 三、插入图表: Word真题中可能会涉及到插入图表的要求,考生需要了解如何插入表格、图片、图表等元素,并进行基本的格式调整,如对表格进行合并、拆分、给图片添加文字说明等。 在回答Word真题的时候,考生需要注意细节,确保完成任务的精确性和准确性。熟练掌握Word的基本操作,并且灵活应用于实际题目中,这样才能够顺利地完成考试。因此,考生在备考过程中需要多进行真题练习,加深对Word操作的理解和熟练程度,确保在考试中能够游刃有余地应对各种题目。 ### 回答2: 高考英语2005-2019真题 Word是高考英语真题的一个分类,包括2005年至2019年期间的真题题目的Word文档。这些真题通常包括听力、阅读、写作和翻译等不同部分。 Word文档是一种常见的电子文件格式,可以在计算机上使用Microsoft Word等软件打开和编辑。高考英语考生通常会下载或购买真题Word文档,以便在电脑上练习真题和进行答题。 通过练习高考英语真题,考生可以更好地了解考试的难度和题型要求,提高自己的应试能力和解题技巧。真题Word文档中的题目通常与高考英语考试内容一致,通过反复练习,考生可以熟悉试题形式、积累词汇和语法知识,并提高自己的阅读、听力和写作能力。 在使用高考英语2005-2019真题Word文档时,考生需要注意保护电脑和软件的安全。建议从正规渠道下载或购买真题Word文档,避免下载来路不明的文件,以免带来风险。此外,考生还应合理安排使用时间,量力而行地进行练习,以确保高效提升自己的英语水平。 总之,高考英语2005-2019真题Word是一种有助于考生备考的资源,通过使用这些真题Word文档进行练习,考生可以提高自己的英语水平和应试能力,为高考取得好成绩打下坚实的基础。 ### 回答3: 高考2005-2019真题word是指高考历年来的真题中,出现的与word处理软件相关的题目。Word是一款常用的文字处理软件,广泛应用于办公、学习等各个领域。根据题目类型的不同,这些与word相关的题目大致可以分为以下几类。 首先,是基础操作类题目。这类题目要求考生熟悉word软件的基本操作,如新建、保存、打开、复制粘贴等。考生需要理解word软件的界面布局和功能按钮的作用。 其次,是文字排版类题目。这类题目考察考生对word软件中文字排版的操作和规则的理解。如要求对一文字进行添加、删除格式、居中等操作。 再次,是表格制作类题目。这类题目考察考生对word软件中表格制作的掌握程度。如要求考生创建一个表格、插入、删除和修改表格内容等。 另外,还有图表制作类题目。这类题目要求考生使用word软件制作各种图表,包括折线图、柱状图、饼图等。考生需要了解图表的创建、设置和修改。 最后,还有使用word软件进行文字编辑和格式化的题目。这类题目考察考生对文字编辑和格式化的掌握程度,如要求对一文字进行加粗、斜体、字号、字体等的设置。 总之,高考2005-2019真题word考查考生对word软件的基本操作、文字排版、表格制作、图表制作和文字编辑等方面的掌握。通过解答这些题目,考生可以提高对word软件的理解和运用能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值