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; }
|