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. Codeforces768B-Code For 1-类似线段树-枚举+单点更新or区间更新

    目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog Problem:Portal传送门  原题目描述在最下面.  每次把\(n\ ...

  2. 纯CSS3制作的“Ribbons”效果

    在看具体每个demo之前,我们一起来看下面一个截图: 上图是一个典型的“Ribbons”各部位的示意图,但每一个“Ribbons”并不会都使用上图示意的各个部分,在下面的实例中大家可以明显的看 到或者 ...

  3. Linux上 安装Sorl4.7 中间件用tomcat

    最近需要用到solr,公司内部搭建了一个solr测试环境. 版本:solr4.7.2 ,tomcat 7.0.55 jdk:1.7_051 解压 solr 和tomcat  这里就不详说. 1.启动t ...

  4. csv转字典

    with open('filename','r') as csv_f: reader = csv.reader(csv_f) fieldnames = next(reader) csv_reader ...

  5. USACO2004 cube stacking /// 带权并查集 oj1302

    题目大意: 以N ( 1 ≤ N ≤ 30,000 )个堆栈开始,每个堆栈包含一个单独的立方体.执行P(1≤ P ≤100,000)的操作. 有两种类型的操作:移动和计数. *在移动操作中,将 包含方 ...

  6. 【学术篇】SDOI2008 山贼集团

    今天一月一号.. 突然想安利一波我的中二的2017总结... 传送门1:codevs 传送门2:luogu 时限5s和1s的区别(你没看我传送门都给的大牛分站了) 现在不仅线筛.. 有负数的快读都打不 ...

  7. delphi 流程单打印

    1.添加声明 f_count1: double; 2.得到拆分页数量 // Modified by 884 2018-04-20 14:50:18 AM0057 with aqTpCount do b ...

  8. Android开发 如何最优的在Activity里释放资源

    前言 当前你已经入门Android开发,开始关注深入的问题,你就会碰到一个Android开发阶段经常碰到的问题,那就是内存泄漏. 其实大多数Android的内存泄漏都是因为activity里的资源释放 ...

  9. SpringBoot防止重复请求,重复表单提交超级简单的注解实现

    1. 注解接口 /** * @description 防止表单重复提交注解 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHO ...

  10. 0704 Process继承实现多进程、Pool进程池,进程间通过队列通信,Pool实现多进程实现复制文件

    通过继承的方式,实现Process多进程 from multiprocessing import Process import time class MyNewProcess(Process): de ...