超级钢琴

有 \(n\) 个音符,编号从 \(1\) 到 \(n\) 。第 \(i\) 个音符的美妙度为 \(A_i\) 。

我们要找到 \(k\) 段不同超级和弦组成的乐曲,每段乐曲的连续音符个数 \(x\) 满足 \(L \le x \le R\) , 求乐曲美妙度的最大值。

注:当且仅当这两个超级和弦所包含的音符集合是相同的。

Solution

我们定义 \(f_{idx,l,r}\) 为左端点为 \(idx\) ,右端点在 \(L \le right \le R\) 区间内的最大值。

我们可以先求出对于每个左端点 \(idx\) ,长度为 \(L \le x \le R\) 中的超级和弦组成的乐曲中的最大值。求出之后我们可以知道,所有符合条件的乐曲美妙度的最大值一定是上面求的所有值中的最大值。 假设 \(f_{idx,l,r}\) 是我们当前求得的最大值,并且取得最大值的右端点为 \(right\)

那么以 \(idx\) 为左端点的区间就可以分裂为 \(f_{idx,l, right - 1}\) 和 \(f_{idx, right + 1, r}\) ,即是对于左端点 \(idx\) 我们把右端点 \(right\) 这个可能性删去。

那么我们考虑维护一个优先队列,先把所有左端点的可能性放进堆中,每次取出堆顶,然后把堆顶分裂,继续加入堆中,当我们取出 k 次之后,就得到了答案。

现在有个问题是怎么求出对于 \(f_{idx,l,r}\) 取得最大值的右端点 \(right\) ,本题的查询是离线,那么我们可以考虑用 ST表 预处理出 \([l,r]\) 区间内的前缀和的最大值的下标。 这样我们既可以求得 \(right\) 又可以求得 最大值 \(s[right] - s[idx - 1]\) 。

CODE

const int N = 5e5 + 10;

int n, k, l, r;
int a[N], s[N];
int f[N][20]; inline void init() {
for(int i = 1; i <= n; i ++ ) f[i][0] = i;
for(int j = 1; (1 << j) <= n; j ++ )
for(int i = 1; i + (1 << j) - 1 <= n; i ++ ){
int x = f[i][j - 1], y = f[i + (1 << (j - 1))][j - 1];
f[i][j] = s[x] >= s[y] ? x : y;
}
} inline int query(int l, int r ) {
int k = log(r - l + 1) / log(2);
int x = f[l][k], y = f[r - (1 << k) + 1][k];
return s[x] >= s[y] ? x : y;
} struct node {
int i, l, r, nw;
bool operator < (const node &t) const {
if(t.nw != nw)
return t.nw > nw;
if(t.i != i)
return t.i > i;
if(t.l != l)
return t.l > l;
return t.r > r;
}
};
priority_queue<node> q;
inline void solve() {
scanf("%d%d%d%d", &n, &k, &l, &r);
for(int i = 1; i <= n; i ++ ) { scanf("%d", &a[i]); s[i] = s[i - 1] + a[i]; }
init();
node res;
for(int i = 1; i <= n; i ++ ) {
res.i = i; res.l = i + l - 1, res.r = min(n, i + r - 1);
if(res.l > n) break;
res.nw = s[query(res.l, min(n, res.r))] - s[i - 1];
q.push(res);
}
LL ans = 0;
int cnt = 0;
while(q.size()) {
auto t = q.top(); q.pop();
ans += t.nw;
cnt ++;
if(cnt >= k) break;
int p = query(t.l, t.r);
if(p > t.l) {
q.push({t.i, t.l, p - 1, s[query(t.l, min(n, p - 1))] - s[t.i - 1]});
}
if(p < t.r) {
q.push({t.i, p + 1, t.r, s[query(p + 1, min(n, t.r))] - s[t.i - 1]});
}
}
printf("%lld\n", ans);
}

Luogu_P2048的更多相关文章

随机推荐

  1. 1336 - Sigma Function

    1336 - Sigma Function   PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB S ...

  2. 关于wlw连接wordpress的问题

    前几天搭建好wordpress博客网站后,一直想和博客园一样,使用wlw发布文章.无奈遇到了难题,一直没有办法解决. 今天我看到一篇博客,遇到问题和我类似:尝试连接到您的日志时出错:基础连接已经关闭: ...

  3. Java中PO、DO、DTO、 VO、 BO、POJO 、DAO、TO的概念

    1.  PO(persistant object) 持久对象 在 O/R 映射的时候出现的概念,如果没有 O/R 映射,没有这个概念存在了. 通常对应数据模型 ( 数据库 ), 本身还有部分业务逻辑的 ...

  4. 第四十六个知识点 在Sigma协议中,正确性,公正性和零知识性意味着什么

    第四十六个知识点 在Sigma协议中,正确性,公正性和零知识性意味着什么 Sigma协议 Sigma协议是Alice想要向Bob证明一些东西的协议(Alice知道一些秘密).他们有下面的一般范式:Al ...

  5. Normalized Cuts and Image Segmentation

    目录 概 主要内容 求解 相似度 总的算法流程 skimage.future.graph.cut Shi J. and Malik J. Normalized cuts and image segme ...

  6. Wavelet Transforms

    目录 目标 小波变换 Scaling Functions Wavelet Functions 二者的联系 离散的情形 高效变换 二维的情形 示例 目标 首先, 既然是变换, 那么就是从一个域到另一个域 ...

  7. 编写Java程序_输入本部门五位员工的薪资,并根据用户输入的序号为指定员工进行提薪。若用户输入序号出现越界,则提示错误。

    要求说明: 输入本部门五位员工的薪资,并根据用户输入的序号为指定员工进行提薪.若用户输入序号出现越界,则提示错误. 运行效果如图: 实现代码: import java.util.Scanner; pu ...

  8. Java初学者作业——编写JAVA程序,要求输入技术部门5位员工的理论成绩和实操成绩,计算并输出各位员工的最终评测成绩。

    返回本章节 返回作业目录 需求说明: 某软件公司要求对技术部门的所有员工进行技能评测,技术评测分为两个部分:理论部分以及实操部分,最终评测成绩=理论成绩×0.4+实操成绩×0.6,要求输入技术部门5位 ...

  9. IIS部署Python-django项目

    1.安装CgiModule模块 首先,IIS要部署django项目,需要CgiModule模块的支持,所以首先我们要确认这个模块是否存在 打开IIS管理器: 我这里是存在的: 如果不存在,可以自行在控 ...

  10. ARP解析MAC地址的全过程(ARP的工作机制)

    以太网环境下,同一个网段的主机之间需要互相知道对方的MAC地址,才能访问. TCP/IP协议栈从上层到下层的封装过程中,第三层封装需要知道目的IP,第二层封装需要知道目的MAC. 目的IP一般由用户手 ...