11.16 GRYZ 模拟赛解题报告
作者:互联网
期望得分:\(100+60+100 = 260pts\)
实际得分:\(100+60+100=260pts\)
今天没有挂分,这是好的。
但今天前两题都十分降智,这是坏的。
T1
转化一下问题就是,找到 \(n\) 段区间,并且要是每段区间的 \(\max - \min\) 在一定范围内。
首先对原数组排序。
然后二分答案,贪心来看,每段区间在数组上都是连续的,所以拿双指针判断即可。
这里我有个降智的地方就是:对一个单调的数组跑单调队列然后还没跑出来。
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int K, n, m;
int a[MAXN];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
return f ? -s : s;
}
bool Check(int mid) {
int res = 0, l = 1, r = l + m - 1;
while(r <= K) {
if(a[r] - a[l] <= mid) {
res++;
l = r + 1, r = r + m;
} else {
l ++, r ++;
}
}
return res >= n;
}
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();
sort(a + 1, a + K + 1);
int l = 0, r = 1000000000, ans = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(Check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", ans);
return 0;
}
/*
5 2 2
7 5 8 2 3
10 3 3
70 22 24 89 83 81 30 3 75 68
10 3 3
6 9 2 4 5 1 6 1 8 2
*/
T2
显然 DP 有 60。
设 \(f_{i,j}\) 表示到 \(i\) 这个点分了 $j $ 段的最大价值。
\[f_{i,j} = \max_{k<i} \{ f_{k,j-1} + s_{k+1,i} \} \]其中 \(s_{i,j}\) 表示 \([l,r]\) 这段区间的或和。
时间复杂度 \(\mathcal O(n^3)\) 。
然后你发现这个从后往前枚举的时候,加的权值最多会变化 \(20\) 次,然后你只从变化的位置转移就好了。
时间复杂度 \(\mathcal O(20n^2)\) 。
这个正解代码我没写,放一个 \(60pts\) 的吧
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int n, K;
int a[MAXN];
int sum[2020][2020];
int f[2020][2020];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
return f ? -s : s;
}
signed main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
n = read(), K = read();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= n; ++i) {
sum[i][i] = a[i];
for(int j = i + 1; j <= n; ++j) {
sum[i][j] = sum[i][j - 1] | a[j];
}
}
memset(f, 128, sizeof f);
f[0][0] = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= K; ++j) {
for(int k = 0; k < i; ++k) {
f[i][j] = max(f[i][j], f[k][j - 1] + sum[k + 1][i]);
}
}
}
printf("%lld\n", f[n][K]);
return 0;
}
/*
5 2
7 5 8 2 3
*/
T3
我们考虑枚举这个二叉树的层数
以最底层为 \(0\) 层
假设我们枚举到了第 \(k\) 层,我们有两个点 \(i,j(i < j)\) ,我们算一下他们会造成多少贡献。
我们一共需要 \(2^{k}\) 个点,去掉当前子树内的根节点还剩 \(x = 2^{k}-1\) 个点。
考虑能有多少种情况。
\[\binom{i-1}{x} \times \binom{j-1 - 2^{k}}{x} \]我们要枚举所有的 \(i,j\) ,则总贡献为
\[\sum_{k = 0}^{K - 1}2^{K - k} \times (n - 2^{k + 1})! \times 2^{k}! \times 2^{k}! \times \displaystyle\sum_{i=1}^{n} \sum_{j = i + 1}^{n}\binom{i-1}{x} \times \binom{j-1 - 2^{k}}{x} \times a_i \times a_j \]你如果暴力计算上面那个式子可以获得 \(60pts\) 的好成绩,时间复杂度 \(\mathcal O(k(2^k)^2)\)
然后你把后面这个东西拿出来:
\[\displaystyle\sum_{i=1}^{n} \binom{i-1}{x} \times a_i \sum_{j = i + 1}^{n} \binom{j-1 - 2^{k}}{x}\times a_j \]考虑对后面这个东西做一个后缀和?
对前面这个东西做一个前缀和也可以。
现在他的复杂度应该是 \(\mathcal O(k2^{k})\) ,可以通过。
/*
Work by: Suzt_ilymtics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define orz cout<<"lkp AK IOI!"<<endl
using namespace std;
const int MAXN = 1e6+5;
const int INF = 1e9+7;
const int mod = 1e9+7;
int n, K, ans = 0;
int a[MAXN];
int fac[MAXN], inv[MAXN];
int sum[MAXN][20];
int read(){
int s = 0, f = 0;
char ch = getchar();
while(!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0', ch = getchar();
return f ? -s : s;
}
void Init() {
int M = 300000;
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= M; ++i) {
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
for(int i = 2; i <= M; ++i) {
inv[i] = inv[i - 1] * inv[i] % mod;
}
}
int C(int n, int m) {
if(n < m) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
signed main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
Init();
K = read(), n = (1 << K);
for(int i = 1; i <= n; ++i) a[i] = read();
for(int k = 0; k < K; ++k) {
for(int i = 1; i <= n; ++i) {
sum[i][k] = sum[i - 1][k] + C(i - 1, (1 << k) - 1) * a[i] % mod;
sum[i][k] %= mod;
}
}
for(int k = 0; k < K; ++k) {
int res = 0;
for(int j = 1; j <= n; ++j) {
int x = (1 << k);
res = res + C(j - 1 - x, x - 1) % mod * a[j] % mod * sum[j - 1][k] % mod;
res = res % mod;
}
res = res * (1 << (K - k)) % mod * fac[n - (1 << k + 1)] % mod;
res = res * fac[1 << k] % mod * fac[1 << k] % mod;
ans = ans + res;
}
ans = ans * inv[n] % mod;
printf("%lld\n", ans);
return 0;
}
标签:11.16,sum,long,times,解题,GRYZ,include,binom,define 来源: https://www.cnblogs.com/Silymtics/p/test-GRYZ20211116.html