【BZOJ3711】Druzyny

题面

bzoj

题解

首先我们有一个\(O(n^2)\)的\(dp\):

设\(f_i\)表示现在已经分好了\(1...i\)的组,且\(i\)作为一组的结尾的最大值,那么转移的话就是对于每个

\(\max\limits_{k=j}^i c_k\leq i-j+1\leq \min\limits_{k=j}^i d_k\)的\(f_{j-1}\)转移一下。

然后这个平方算法减下枝就艹过去了

然后想想怎么优化这个东西。

令\(pre_i\)表示在仅考虑\(d\)的限制之下,对于某一右端点\(i\)可以转移过来的左端点\(j-1\)的最靠左的位置,那么上文的\(j\)对应的就是\([j+1,i]\)这个区间,而且\(pre\)肯定也是单调不降的。

我们考虑在这样子处理\(d\)限制的基础之上解决\(c\)限制,对于一个点的转移,肯定只与一段区间内\(c\)的最大值有关,考虑最大值分治。假设对于一段区间\([l,r]\),\([l+1,r]\)的\(c\)所处的最大值位置为\(p\)(由上文所述,\(f_l\)是否能转移到\(f_r\)不取决于\(l\)的情况),然后递归处理\([l,p)\)再对于\(j\in [l,p)\),我们要求出\(f_j\)对于\(f_i,i\in [p,r]\)的贡献,最后递归\([p,r]\)。

那么问题的关键在于对于\(\forall i\in [p,r]\),\(pre_i\)不降,在复杂度正确的情况下求出\(\forall j\in [l,p)\)对其的贡献。

下面分为四种情况解决这个问题:

\(\text{Case 1:}\)

\(pre_i\leq l,i-p+1< c_p\),此时这种\(i\)会由\([l...x](x< p)\)这段前缀转移过来,第一次用线段树找到最开始的前缀最大值然后随着\(i\)增加往后面推可以\(O(1)\)转移。

这种情况只会\(O(\log n)\)查一次以及一次\(\min(p-l,r-p+1)\)遍历一次,与最大值分治复杂度一致。

\(\text{Case 2:}\)

\(pre_i\leq l,i-p+1\geq c_p\),这时候整个左区间都能转移过来,二分一下\(pre_i\leq l\)最大的\(i\)然后线段树区间修改即可。

这种情况只会\(O(\log n)\)改一次以及\(O(\log n)\)二分一次。

\(\text{Case 3:}\)

\(pre_i>l\),直接暴力查线段树区间即可。

对于\(\forall i\),分治时只会出现至多一次这样的情况,总复杂度\(O(n\log n)\)。

\(\text{Case 4:}\)

直接退出即可。

最后这题卡空间只能用线段树求区间最值,复杂度\(O(n\log n)\)在上面已经分析过了。

代码

这里是\(O(n^2)\)艹过去的

\(O(n\log n)\):

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int INF = 1e9;
const int Mod = 1e9 + 7;
const int MAX_N = 1e6 + 5;
int N, c[MAX_N], d[MAX_N], pre[MAX_N];
struct Data { int f, g; } F[MAX_N];
Data operator + (const Data &l, const Data &r) {
if (l.f == r.f) return (Data){l.f, (l.g + r.g) % Mod};
else {
if (l.f < r.f) return (Data){r.f, r.g};
else return (Data){l.f, l.g};
}
}
#define lson (o << 1)
#define rson (o << 1 | 1)
namespace SGT1 {
int maxp[MAX_N << 2];
void build(int o, int l, int r) {
if (l == r) return (void)(maxp[o] = l);
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
maxp[o] = c[maxp[lson]] > c[maxp[rson]] ? maxp[lson] : maxp[rson];
}
int query(int o, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return maxp[o];
int mid = (l + r) >> 1, res = 0;
if (ql <= mid) res = query(lson, l, mid, ql, qr);
if (qr > mid) {
int p = query(rson, mid + 1, r, ql, qr);
res = c[res] > c[p] ? res : p;
}
return res;
}
}
namespace SGT2 {
Data Max[MAX_N << 2], Tag[MAX_N << 2];
void pushup(int o) { Max[o] = Max[lson] + Max[rson]; }
void puttag(int o, Data v) { Tag[o] = Tag[o] + v, Max[o] = Max[o] + v; }
void pushdown(int o) {
puttag(lson, Tag[o]);
puttag(rson, Tag[o]);
Tag[o] = (Data){-INF, 0};
}
void build(int o, int l, int r) {
Max[o] = Tag[o] = (Data){-INF, 0};
if (l == r) return ;
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
}
void upd(int pos) {
int o = 1, l = 0, r = N;
while (l < r) {
pushdown(o);
int mid = (l + r) >> 1;
if (pos <= mid) o = lson, r = mid;
else o = rson, l = mid + 1;
}
F[pos] = Max[o] = F[pos] + Tag[o];
while (o >>= 1) pushup(o);
}
void modify(int o, int l, int r, int ql, int qr, Data v) {
if (ql <= l && r <= qr) return puttag(o, v);
pushdown(o);
int mid = (l + r) >> 1;
if (ql <= mid) modify(lson, l, mid, ql, qr, v);
if (qr > mid) modify(rson, mid + 1, r, ql, qr, v);
pushup(o);
}
Data query(int o, int l, int r, int ql, int qr) {
if (ql > qr) return (Data){-INF, 0};
if (ql <= l && r <= qr) return Max[o];
pushdown(o);
int mid = (l + r) >> 1;
Data res = (Data){-INF, 0};
if (ql <= mid) res = res + query(lson, l, mid, ql, qr);
if (qr > mid) res = res + query(rson, mid + 1, r, ql, qr);
return res;
}
}
void init() {
SGT1::build(1, 1, N);
SGT2::build(1, 0, N);
static priority_queue<int, vector<int>, greater<int> > q1, q2;
for (int i = 1; i <= N; i++) {
pre[i] = pre[i - 1];
q1.push(d[i]);
while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();
while (q1.top() < i - pre[i]) {
q2.push(d[++pre[i]]);
while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop();
}
}
}
void Div(int l, int r) {
if (l == r) return SGT2::upd(l);
int p = SGT1::query(1, 1, N, l + 1, r);
Div(l, p - 1);
int pos = max(p, l + c[p]);
Data nw = SGT2::query(1, 0, N, l, pos - c[p] - 1);
while (pos <= r && pre[pos] <= l && pos - c[p] < p) {
nw = nw + F[pos - c[p]];
F[pos] = F[pos] + (Data){nw.f + 1, nw.g};
++pos;
}
if (pos <= r && pre[pos] <= l) {
int L = pos, R = r;
while (L < R) {
int M = (L + R + 1) >> 1;
if (pre[M] <= l) L = M;
else R = M - 1;
}
SGT2::modify(1, 0, N, pos, L, (Data){nw.f + 1, nw.g});
pos = L + 1;
}
while (pos <= r && pre[pos] < p) {
nw = SGT2::query(1, 0, N, pre[pos], min(pos - c[p], p - 1));
F[pos] = F[pos] + (Data){nw.f + 1, nw.g};
++pos;
}
Div(p, r);
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
N = gi();
for (int i = 1; i <= N; i++) c[i] = gi(), d[i] = gi();
for (int i = 1; i <= N; i++) F[i] = (Data){-INF, 0};
F[0] = (Data){0, 1};
init();
Div(0, N);
if (F[N].f <= 0) puts("NIE");
else printf("%d %d\n", F[N].f, F[N].g);
return 0;
}

【BZOJ3711】Druzyny的更多相关文章

  1. Python高手之路【六】python基础之字符串格式化

    Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This ...

  2. 【原】谈谈对Objective-C中代理模式的误解

    [原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...

  3. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. 【调侃】IOC前世今生

    前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...

  6. Python高手之路【三】python基础之函数

    基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...

  7. Python高手之路【一】初识python

    Python简介 1:Python的创始人 Python (英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/), 是一种解释型.面向对象.动态数据类型的高级程序设计语言,由荷兰人Guido ...

  8. 【开源】简单4步搞定QQ登录,无需什么代码功底【无语言界限】

    说17号发超简单的教程就17号,qq核审通过后就封装了这个,现在放出来~~ 这个是我封装的一个开源项目:https://github.com/dunitian/LoTQQLogin ————————— ...

  9. 【原】FMDB源码阅读(二)

    [原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...

随机推荐

  1. 使用eclipse git插件合并merge部分代码方法

    当有一个父项目,它的下面有多个子项目:或者一个项目下边,只想合并部分路径,甚至部分文件的内容,使用下边的方法可以达到目的,特此记录: 1.主项目右键 -> team -> remove f ...

  2. Spring Boot 如何干掉 if else?

    需求 这里虚拟一个业务需求,让大家容易理解.假设有一个订单系统,里面的一个功能是根据订单的不同类型作出不同的处理. 订单实体: service接口: 传统实现 根据订单类型写一堆的if else: 策 ...

  3. .net持续集成cake篇之常见文件及路径操作

    系列目录 Cake常见文件和路径操作 在自动化构建任务里,很多操作都是跟文件打交道,比如文件打包,文件压缩,文件归档,文件传输,目录清理等.本节介绍一些cake里常见的文件操作方法 Cake相对路径问 ...

  4. eclipse使用maven,启动工程tomcat报错:java.lang.ClassNotFoundException: org.springframework.web.context.Contex

    maven是个不错的管理jar包工具,但是我们在eclipse使用maven时,总是遇上这样那样的问题,比如今天,我编译工程,启动过后,tomcat报错:java.lang.ClassNotFound ...

  5. 用JavaScript带你体验V8引擎解析标识符

    上一篇讲了字符串的解析过程,这一篇来讲讲标识符(IDENTIFIER)的解析. 先上知识点,标识符的扫描分为快解析和慢解析,一旦出现Ascii编码大于127的字符或者转义字符,会进入慢解析,略微影响性 ...

  6. WPF 精修篇 管理资源字典

    原文:WPF 精修篇 管理资源字典 样式太多  每个界面可能需要全局的样式 有没有肯能 WPF 中的样式 像Asp.net中 的CSS一样管理那 有的 有资源字典 BurshDictionary &l ...

  7. MVC里模型常用的一些操作

    学习也是做买卖,归根到底还是学习成本的问题. 下面把微软集合类型的增删改查稍微罗列一下,大家看看它能带来的便利,和你要学的新东西,还有风险(纯粹的数据操作,不用框架,风险其实不大)相比,是否值得.来决 ...

  8. oracle中查询一月的时间补0的问题

    sql语句 select NVL(B.weight, 0),A.time from (SELECT TO_CHAR(TO_DATE(开始时间, 'yyyy-MM-dd') + ROWNUM - 1, ...

  9. 使用Docker Compose 部署Nexus后提示:Unable to create directory /nexus-data/instance

    场景 Ubuntu Server 上使用Docker Compose 部署Nexus(图文教程): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/ ...

  10. Python检测URL状态

    需求:Python检测URL状态,并追加保存200的URL 代码一: #! /usr/bin/env python #coding=utf-8 import sys import requests d ...