Educational Codeforces Round 69 题解

题目编号 A B C D E F
完成情况 -

D. Yet Another Subarray Problem

一个子数组的价值为:

\[\sum_{i=l}^{r} a[i] - k\lceil{\frac{r-l+1}{m}}\rceil
\]

求解其最大值。子数组可以为空,此时价值为0.

\(r-l+1\)自然是子数组的长度\(len\),可以发现每当\(len\)增加\(m\)后,\(\lceil{\frac{r-l+1}{m}}\rceil\)会增加1。也就是说,子数组的权值受到长度的影响。\(dp[len][n]\)显然是不行的。实际上转移的时候,\(dp[len][n]\)从\(dp[len-1][n-1]\)转移过来,我们真正关心的是\(len\)能否被\(m\)整除,从而要多减一个\(k\)。于是只需要存储\(len mod m\)的余数就可以了。

\[dp[i][j]:include\ a[i]\ and\ len\ mod\ m\ =\ j
\]

\[if\ m\ ==\ 0\ OR\ j\ ==\ 1\ \ \ dp[i][j]\ =\ max\{dp[i\ -\ 1][0],\ 0\}\ +a[i]\ -\ k
\]

\[else\ if\ j==0\ \ \ dp[i][j]\ =\ dp[i-1][m - 1]\ +\ a[i]
\]

\[else\ \ \ dp[i][j]=dp[i-1][j-1]+a[i]
\]

一定要留心\(m=1\)的情况!单独考虑

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath> long long max(long long a, long long b){return a > b ? a : b;}
long long min(long long a, long long b){return a < b ? a : b;}
void swap(long long &a, long long &b){long long tmp = a;a = b;b = tmp;}
long long lowbit(long long &x){return x & (-x);}
void read(long long &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
} const long long INF = 0x3f3f3f3f3f3f3f3f;
const long long MAXN = 300000 + 10;
const long long MAXM = 10; long long dp[MAXN][MAXM + 10], n, m, k, a[MAXN];
//dp[i][j]表示以i为结尾或者空串,长度余数为j的最大值
//dp[i][j] = dp[i - 1][j - 1] + a[i]
//dp[i][j] = dp[i - 1][j - 1] + a[i] - k int main()
{
read(n), read(m), read(k);
long long ans = 0;
for(long long i = 1;i <= n;++ i) read(a[i]); for(int i = 0;i <= n;++ i)
for(int j = 0;j < m;++ j)
dp[i][j] = -INF; for(long long i = 1;i <= n;++ i)
for(long long j = 0;j < m;++ j)
{
if(j == 1 || m == 1)
dp[i][j] = max(dp[i - 1][0], 0) + a[i] - k;
else if(j == 0)
dp[i][j] = dp[i - 1][m - 1] + a[i];
else
dp[i][j] = dp[i - 1][j - 1] + a[i];
ans = max(ans, dp[i][j]);
}
printf("%I64d", ans);
return 0;
}

E. Culture Code

有一些套娃,每个套娃都有\(in_i\)和\(out_i\)两个属性,只有\(in_i\ \geq\ out_j\),套娃\(j\)才能套在\(i\)的里面。一个相互嵌套的套娃集合的额外值定义为:

\[in_i\ +\ (in_{i+1}\ -out_{i})\ +\ (in_{i+2}\ -out_{i+1})\ +\ (in_{i+3}\ -out_{i+2})\ +\ \cdots\ +\ (in_{j}\ -out_{j-1})\
\]

一个套娃集合为极大集合,当且仅当不能再套在里面或外面任意另一个套娃。

求套娃极大集合的最小额外值的方案数

将套娃按照\(in\)降序排序

\(dp[0][i]\)表示前i个套娃,第\(i\)个套娃在最里面的最小额外值

\(dp[1][i]\)表示上面这个最小额外值的方案

\[dp[0][i]\ =\ min\{dp[0][j]\}\ -\ (out[i]\ -\ in[i])\
\]

相当于把第\(j\)个下面塞上\(i\),额外值减小。要求$in[j] \geq\ out[i]\ $ 直接二分就能找到合法区间\(1~x\),用线段树维护前缀最小和最小的个数。

其实也不用二分,在线段树询问操作中实现二分即可。

求最小的过程保证了对于前\(i\)个,第\(i\)个是极大集合。证明方法采用数学归纳法。第一个本身就是极大集合,从第二个开始,对于第\(i个\),因为里面不能再套,外面在\(dp\)后已经套过一个\(j\),\(j\)是前\(j\)个中的最大集合(假设条件),而\(j+1~i-1\)中的\(in\)小于等于\(in[j]\),因而小于\(out[j]\),也不能再套了

最后计算结果时,把所有\(dp[0][i]==min\)的\(dp[1][i]\)累加。能证明最小的\(dp[0][i]\)一定是极大集合,首先放在\(i\)后面的只能往它下面套,会让答案减小,后面的不能

再套了;前面的能不能套再套,证明同上

初始状态:\(dp[0][1] = in[1]\)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath> long long max(long long a, long long b){return a > b ? a : b;}
long long min(long long a, long long b){return a < b ? a : b;}
void swap(long long &a, long long &b){long long tmp = a;a = b;b = tmp;}
long long lowbit(long long &x){return x & (-x);}
void read(long long &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
} const long long INF = 0x3f3f3f3f3f3f3f3f;
const long long MAXN = 200000 + 10;
const long long MOD = 1e9 + 7; long long n, in[MAXN], out[MAXN], id[MAXN]; bool cmp(long long a, long long b)
{
return in[a] > in[b];
} struct Node
{
long long cnt, mi;
}node[MAXN << 2]; Node merge(Node& a, Node& b)
{
Node re;
if(a.mi == b.mi)
re.mi = a.mi,
re.cnt = a.cnt + b.cnt,
re.cnt >= MOD ? re.cnt -= MOD : 0;
else if(a.mi < b.mi)
re = a;
else
re = b;
return re;
} void pushup(long long o)
{
node[o] = merge(node[o << 1], node[o << 1 | 1]);
return ;
} void build(long long o = 1, long long l = 1, long long r = n)
{
if(l == r)
{
node[o].mi = INF, node[o].cnt = 0;
return ;
}
long long mid = (l + r) >> 1;
build(o << 1, l, mid);
build(o << 1 | 1, mid + 1, r);
pushup(o);
} //在p位置更新最小值为x,方案数为y
void modify(long long p, long long x, long long y, long long o = 1, long long l = 1, long long r = n)
{
if(l == r)
{
if(node[o].mi == x) node[o].cnt += y;
else node[o].mi = x, node[o].cnt = y;
return ;
}
long long mid = (l + r) >> 1;
if(p <= mid) modify(p, x, y, o << 1, l, mid);
else modify(p, x, y, o << 1 | 1, mid + 1, r);
pushup(o);
return ;
} //1到最后一个大于等于x的位置
Node ask(long long x, long long rr, long long o = 1, long long l = 1, long long r = n)
{
if(in[id[r]] >= x && rr >= r) return node[o];
long long mid = (l + r) >> 1;
Node a, b;
a.mi = INF, b.mi = INF;
if(in[id[l]] >= x) a = ask(x, rr, o << 1, l, mid);
if(in[id[mid + 1]] >= x && rr > mid) b = ask(x, rr, o << 1 | 1, mid + 1, r);
return merge(a, b);
} Node dp[MAXN];
long long mi = INF, ans = 0; int main()
{
read(n);
for(long long i = 1;i <= n;++ i)
read(out[i]), read(in[i]), id[i] = i;
std::sort(id + 1, id + 1 + n, cmp);
build();
modify(1, in[id[1]], 1);
dp[1].mi = in[id[1]], dp[1].cnt = 1;
for(long long i = 2;i <= n;++ i)
{
dp[i] = ask(out[id[i]], i - 1);
if(dp[i].mi == INF) dp[i].mi = in[id[i]], dp[i].cnt = 1;
else dp[i].mi -= out[id[i]] - in[id[i]];
modify(i, dp[i].mi, dp[i].cnt);
}
for(long long i = 1;i <= n;++ i)
mi = min(mi, dp[i].mi);
for(long long i = 1;i <= n;++ i)
if(dp[i].mi == mi)
ans += dp[i].cnt,
ans >= MOD ? ans -= MOD : 0;
printf("%I64d", ans);
return 0;
}

---恢复内容结束---

#Educational Codeforces Round 69 题解

题目编号 A B C D E F
完成情况 -

D. Yet Another Subarray Problem

一个子数组的价值为:

\[\sum_{i=l}^{r} a[i] - k\lceil{\frac{r-l+1}{m}}\rceil
\]

求解其最大值。子数组可以为空,此时价值为0.

\(r-l+1\)自然是子数组的长度\(len\),可以发现每当\(len\)增加\(m\)后,\(\lceil{\frac{r-l+1}{m}}\rceil\)会增加1。也就是说,子数组的权值受到长度的影响。\(dp[len][n]\)显然是不行的。实际上转移的时候,\(dp[len][n]\)从\(dp[len-1][n-1]\)转移过来,我们真正关心的是\(len\)能否被\(m\)整除,从而要多减一个\(k\)。于是只需要存储\(len mod m\)的余数就可以了。

\[dp[i][j]:include\ a[i]\ and\ len\ mod\ m\ =\ j
\]

\[if\ j==0\ dp[i][j]\ =\ max \{ dp[i-1][m-1],0 \} +a[i]-k \}
\]

\[else\ dp[i][j]=dp[i-1][j-1]+a[i]
\]

初始状态:因为\(dp\)中有很多不合题意的量,我们不能用这些量去进行转移,于是初始全部赋值为\(-INF\)。\(dp[1][0]\)和\(dp[1][1]\)是唯两个满足条件的第一维是1的量,为了让他们赋值正确,考虑让dp[0][m-1]=0

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath> long long max(long long a, long long b){return a > b ? a : b;}
long long min(long long a, long long b){return a < b ? a : b;}
void swap(long long &a, long long &b){long long tmp = a;a = b;b = tmp;}
long long lowbit(long long &x){return x & (-x);}
void read(long long &x)
{
x = 0;char ch = getchar(), c = ch;
while(ch < '0' || ch > '9') c = ch, ch = getchar();
while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
if(c == '-') x = -x;
} const long long INF = 0x3f3f3f3f3f3f3f3f;
const long long MAXN = 300000 + 10;
const long long MAXM = 10; long long dp[MAXN][MAXM + 10], n, m, k, a[MAXN];
//dp[i][j]表示以i为结尾或者空串,长度余数为j的最大值
//dp[i][j] = dp[i - 1][j - 1] + a[i]
//dp[i][j] = dp[i - 1][j - 1] + a[i] - k if j == 0 int main()
{
read(n), read(m), read(k);
long long ans = 0;
for(long long i = 1;i <= n;++ i) read(a[i]); for(int i = 0;i <= n;++ i)
for(int j = 0;j < m;++ j)
dp[i][j] = -INF;
dp[0][m - 1] = 0; for(long long i = 1;i <= n;++ i)
for(long long j = 0;j < m;++ j)
{
if(j == 0)
dp[i][j] = max(dp[i - 1][m - 1] + a[i] , a[i]) - k;
else
dp[i][j] = dp[i - 1][j - 1] + a[i];
ans = max(ans, dp[i][j]);
}
printf("%I64d", ans);
return 0;
}

Educational Codeforces Round 69 D E的更多相关文章

  1. Educational Codeforces Round 69 (Rated for Div. 2) E. Culture Code

    Educational Codeforces Round 69 (Rated for Div. 2) E. Culture Code 题目链接 题意: 给出\(n\)个俄罗斯套娃,每个套娃都有一个\( ...

  2. Educational Codeforces Round 69 D. Yet Another Subarray Problem

    Educational Codeforces Round 69 (Rated for Div. 2) D. Yet Another Subarray Problem 题目链接 题意: 求\(\sum_ ...

  3. Educational Codeforces Round 69 (Rated for Div. 2)

                                                                                                  A. DIY ...

  4. Educational Codeforces Round 69 (Rated for Div. 2) D. Yet Another Subarray Problem 背包dp

    D. Yet Another Subarray Problem You are given an array \(a_1, a_2, \dots , a_n\) and two integers \( ...

  5. Educational Codeforces Round 69 (Rated for Div. 2) C. Array Splitting 水题

    C. Array Splitting You are given a sorted array

  6. Educational Codeforces Round 69

    目录 Contest Info Solutions A. DIY Wooden Ladder B. Pillars C. Array Splitting D. Yet Another Subarray ...

  7. Educational Codeforces Round 69 (Rated for Div. 2) A~D Sloution

    A. DIY Wooden Ladder 题意:有一些不能切的木板,每个都有一个长度,要做一个梯子,求梯子的最大台阶数 做梯子的木板分为两种,两边的两条木板和中间的若干条台阶木板 台阶数为 $k$ 的 ...

  8. Educational Codeforces Round 69 E - Culture Code (最短路计数+线段树优化建图)

    题意:有n个空心物品,每个物品有外部体积outi和内部体积ini,如果ini>outj,那么j就可以套在i里面.现在我们要选出n个物品的一个子集,这个子集内的k个物品全部套在一起,且剩下的物品都 ...

  9. Educational Codeforces Round 69 (Rated for Div. 2)D(DP,思维)

    #include<bits/stdc++.h>using namespace std;int a[300007];long long sum[300007],tmp[300007],mx[ ...

随机推荐

  1. APIO 2007 风铃

    题目描述 你准备给弟弟 Ike 买一件礼物,但是,Ike 挑选礼物的方式很特别:他只喜欢那些能被他排成有序形状的东西. 你准备给 Ike 买一个风铃.风铃是一种多层的装饰品,一般挂在天花板上. 每个风 ...

  2. error C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead.【转载】

    文章出处https://blog.csdn.net/qq_38721302/article/details/82850292 今天编写C++程序在使用头文件#include<cstring> ...

  3. 在 input 的 placeholder中 使用iconfont

    写在前面 产品要求放大镜和文字放在一起.用定位,位置不准确,就会导致手机上错位,丑的一批. 进入正题 如何在input的 placeholder 中使用图标呢? 以阿里巴巴的矢量图标库为例, 现在有三 ...

  4. 求树的最大独立集,最小点覆盖,最小支配集 贪心and树形dp

    目录 求树的最大独立集,最小点覆盖,最小支配集 三个定义 贪心解法 树形DP解法 (有任何问题欢迎留言或私聊&&欢迎交流讨论哦 求树的最大独立集,最小点覆盖,最小支配集 三个定义 最大 ...

  5. maven-dependencyManagement和dependencies区别

    在多模块的maven项目中,如果各个子项目的依赖包相同但版本不同的话,对于测试.发布和管理非常困难 dependencyManagement就是起统一版本作用的一个标签,好处有2,一是统一版本,二是子 ...

  6. 科普帖:深度学习中GPU和显存分析

    知乎的一篇文章: https://zhuanlan.zhihu.com/p/31558973 关于如何使用nvidia-smi查看显存与GPU使用情况,参考如下链接: https://blog.csd ...

  7. mysql 需要掌握的重点

    1. 安装mysql:     google it.2. 新建database,table: create database database_name;create table table_name ...

  8. Could not create cudnn handle: CUDNN_STATUS_INTERNAL_ERROR tensorflow-1.13.1和1.14windows版本目前不支持CUDA10.0

    报错出现 Could not create cudnn handle: CUDNN_STATUS_INTERNAL_ERROR tensorflow-1.13.1和1.14windows版本目前不支持 ...

  9. OC开发系列-成员变量的作用域

    成员变量的作用域 OC中成员变量有四种作用域,同时每一种作用域对应着响应的关键字. * @private:自能在当前类的实现@implementation中访问 * @protected: 可以在当前 ...

  10. 2018-8-10-使用-IncrediBuild-提升-VisualStudio-编译速度

    title author date CreateTime categories 使用 IncrediBuild 提升 VisualStudio 编译速度 lindexi 2018-08-10 19:1 ...