https://download.csdn.net/download/qq_45721135/11866519
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
考场思路
仔细读题,就发现这道题目是要让最大值最小,于是貌似就可以去二分答案。可以发现,如果把所有可以填的数排一下序,那么最优方法中每一行所选的数在排好序的序列中一定是连续的,所以可以贪心去取每一行的数,来验证答案。
考场代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
   | #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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
   | #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)$的暴力。
状态设计:用$f[i][j]$表示把区间1~i分成j段能获得的最大价值。
转移:$f[i][j]=\max(f[k][j-1]+l[k+1][i]) $,其中$l[i][j]$表示对区间[i,j]按位或的结果。
考场代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
   | #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]$j不变,关于i单调递减
std
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
   | #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了。大概就按照线段树建树的方式瞎搞了一下,貌似还搞错了……
考场代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
   | #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; }
   | 
 
题解
- 30分:全排列,暴力求期望
 
- 60分:
 
![在这里插入图片描述]()
![在这里插入图片描述]()
100分
 听不懂……
 
std
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
   | #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; }
   |