简要题意

有 \(n\) 个人,把他们划分成尽可能多的区间,其中第 \(i\) 个人要求它所在的区间长度大于等于 \(c_i\),小于等于 \(d_i\),求最多的区间数量以及如此划分的方案数。

数据范围:\(1\le n \le 10^6, 1\le c_i, d_i\le n\)。时间限制7s(优秀解500-700ms,我的垃圾大常数实现2s,我都觉得写假了)。

题解

方案数是 trivial 的,实现一个 Data 类,合并信息的时候同时维护最大值和方案数即可。注意信息失去了幂等性。

首先考虑朴素 DP,设 \(f_i\) 表示前 \(i\) 个数可以划分成的最大区间数。转移枚举上一段的端点即可。时间复杂度 \(\Theta(n^2)\)。

下面考虑优化。注意到线性结构,转移彼此独立,考虑分治优化。题解区许多解法似乎是标准 CDQ 分治做法,然而时间复杂度是双 \(\log\) 的同时实现也较为复杂。我没有研究。

这题处理的难点在于我要同时维护 \(c\) 和 \(d\) 两个方向的限制,能转移到 \(i\) 的所有 \(j\) 分布很诡异,而 CDQ 分治并不能帮助改善这一点。而我们对于 \(c, d\) 关注的只有最值。这启示我们使用最值分治的做法,将其中一个的最值对应点放到分治中心,这样所有左区间到右区间的转移都会跨越这个点,它又是最值,相当于这一维限制被固定了。观察题目可以发现,\(d\) 作为上界限制,只考虑它限制下能对 \(i\) 进行转移的 \(j\) 一定是 \([0, i - 1]\) 的一段后缀,设为 \([pre_i, i - 1]\),这个东西很好预处理,并且很优美(注意到 \(pre_i\) 不降),所以我们考虑固定 \(c\),当前分治区间设为 \([l, r]\),中点设为 \(mid\)(注意左区间是 \([l, mid)\)),那么能转移到右区间 \(i\) 的 \(j\) 所属区间是 \([l, mid)\cap [pre_i, i - c_{mid}]\)。

然而最值分治失去了分治层数 \(\log\) 的特性,一般地需要在合并时精细实现来避免退化成平方(我就退化过)。当然,分治区间总个数仍然保证是 \(\Theta(n)\) 的。接下来考虑分类转移:

  1. \(i < l + c_mid\) 或者 \(pre_i \ge mid\):很容易排除掉。

  2. \(pre_i < l, i - c_{mid} < mid\):区间是 \([l, mid)\) 的前缀,随着 \(i\) 不断增加,右端点只会每次移动 1,那么第一次在线段树上查一下,后面就直接用 \(f_j\) 来 \(\Theta(1)\) 更新即可。这一部分的时间复杂度:第一次查线段树 \(\Theta(n\log n)\),后面的更新次数只会是左右区间长度的 \(\min\),类比启发式合并可以得到时间复杂度是 \(\mathrm O(n\log n)\)。

  3. \(pre_i < l, i - c_{mid} \ge mid\):区间是 \([l, mid)\),二分一下右边能被这样更新的 \(i\),一定也是个区间,线段树上区间查一下,区间取 \(max\) 一下,\(\Theta(n\log n)\)。

  4. \(pre_i \in [l, mid), i - c_{mid} \ge mid\):全部暴力 \(\Theta(\log n)\),这是因为对于一个 \(i\),它所属的所有分治右区间,所对应的分治左区间两两不交(可以画出分治树看一下),所以 \(pre_i\) 只属于其中某一个,于是每个 \(i\) 只会这样暴力转移一次。

于是这样做完了,总复杂度 \(\Theta(n\log n)\)。

代码

// Author: kyEEcccccc

#include <bits/stdc++.h>

using namespace std;

using LL = long long;
using ULL = unsigned long long; #define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1) const int N = 1000005, MOD = 1000000007; int n, c[N], d[N]; struct Data { int mx; LL s; }; Data mer(Data x, Data y)
{
Data r = {max(x.mx, y.mx), 0};
if (x.mx == r.mx) r.s += x.s;
if (y.mx == r.mx) (r.s += y.s) %= MOD;
return r;
} struct SegTree
{
Data x[N << 2], tg[N << 2]; void init(int p, int cl, int cr)
{
x[p] = tg[p] = {-n, 0};
if (cl == cr) return;
int cm = cl + cr >> 1;
init(p << 1, cl, cm);
init(p << 1 | 1, cm + 1, cr);
} void pushdown(int p)
{
Data d = tg[p]; tg[p] = {-n, 0};
x[p << 1] = mer(x[p << 1], d);
x[p << 1 | 1] = mer(x[p << 1 | 1], d);
tg[p << 1] = mer(tg[p << 1], d);
tg[p << 1 | 1] = mer(tg[p << 1 | 1], d);
} Data get_max(int p, int cl, int cr, int l, int r)
{
if (l > r) return {-n, 0};
if (cl >= l && cr <= r) return x[p];
pushdown(p);
int cm = cl + cr >> 1;
if (l <= cm && r > cm)
{
return mer(get_max(p << 1, cl, cm, l, r),
get_max(p << 1 | 1, cm + 1, cr, l, r));
}
else if (l <= cm) return get_max(p << 1, cl, cm, l, r);
else return get_max(p << 1 | 1, cm + 1, cr, l, r);
} void assign_max(int p, int cl, int cr, int l, int r, Data d)
{
if (l > r) return;
if (cl >= l && cr <= r)
{
x[p] = mer(x[p], d), tg[p] = mer(tg[p], d);
return;
}
pushdown(p);
int cm = cl + cr >> 1;
if (l <= cm) assign_max(p << 1, cl, cm, l, r, d);
if (r > cm) assign_max(p << 1 | 1, cm + 1, cr, l, r, d);
x[p] = mer(x[p << 1], x[p << 1 | 1]);
}
} sgt; int pre[N];
Data f[N]; struct SegTree2
{
int x[N << 2]; void init(int p, int cl, int cr)
{
if (cl == cr) return x[p] = cl, void();
int cm = cl + cr >> 1;
init(p << 1, cl, cm);
init(p << 1 | 1, cm + 1, cr);
if (c[x[p << 1]] > c[x[p << 1 | 1]]) x[p] = x[p << 1];
else x[p] = x[p << 1 | 1];
} int get_max(int p, int cl, int cr, int l, int r)
{
if (cl >= l && cr <= r) return x[p];
int cm = cl + cr >> 1;
if (l <= cm && r > cm)
{
int a = get_max(p << 1, cl, cm, l, r),
b = get_max(p << 1 | 1, cm + 1, cr, l, r);
return c[a] > c[b] ? a : b;
}
else if (l <= cm) return get_max(p << 1, cl, cm, l, r);
else return get_max(p << 1 | 1, cm + 1, cr, l, r);
}
} sgt2; void solve(int l, int r)
{
if (l == r)
{
Data t = f[l];
f[l] = mer(f[l], sgt.get_max(1, 0, n, l, l));
sgt.assign_max(1, 0, n, l, l, t);
return;
} int mid = sgt2.get_max(1, 1, n, l + 1, r) - 1, mxc = c[mid + 1]; solve(l, mid); int p = max(mid + 1, l + mxc);
Data cur_x = {INT_MIN, 0};
while (p <= r && p - mxc < mid && pre[p] <= l)
{
if (cur_x.mx == INT_MIN) cur_x = sgt.get_max(1, 0, n, l, p - mxc);
else cur_x = mer(cur_x, f[p - mxc]);
Data x = cur_x; ++x.mx;
f[p] = mer(f[p], x);
++p;
}
if (p > r) goto SOLVE_R; if (pre[p] <= l)
{
int cl = p, cr = r, ca = p;
while (cl <= cr)
{
int cm = cl + cr >> 1;
if (pre[cm] <= l) ca = cm, cl = cm + 1;
else cr = cm - 1;
}
Data x = sgt.get_max(1, 0, n, l, mid); ++x.mx;
sgt.assign_max(1, 0, n, p, ca, x);
p = ca + 1;
}
if (p > r) goto SOLVE_R; while (p <= r && pre[p] <= mid)
{
Data x = sgt.get_max(1, 0, n, pre[p], min(mid, p - mxc));
++x.mx;
f[p] = mer(f[p], x);
++p;
} SOLVE_R: solve(mid + 1, r);
} struct SegTree3
{
int x[N << 2]; void init(int p, int cl, int cr)
{
if (cl == cr) return x[p] = d[cl], void();
int cm = cl + cr >> 1;
init(p << 1, cl, cm);
init(p << 1 | 1, cm + 1, cr);
x[p] = min(x[p << 1], x[p << 1 | 1]);
} int get_min(int p, int cl, int cr, int l, int r)
{
if (cl >= l && cr <= r) return x[p];
int cm = cl + cr >> 1;
if (l <= cm && r > cm)
{
return min(get_min(p << 1, cl, cm, l, r),
get_min(p << 1 | 1, cm + 1, cr, l, r));
}
else if (l <= cm) return get_min(p << 1, cl, cm, l, r);
else return get_min(p << 1 | 1, cm + 1, cr, l, r);
}
} sgt3; signed main(void)
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(nullptr); cin >> n;
F(i, 1, n) cin >> c[i] >> d[i]; sgt3.init(1, 1, n);
pre[1] = 0;
F(i, 2, n)
{
pre[i] = pre[i - 1];
while (sgt3.get_min(1, 1, n, pre[i] + 1, i) < i - pre[i]) ++pre[i];
// cout << i << ": " << pre[i] << '\n';
} f[0] = {0, 1};
F(i, 1, n) f[i] = {-n, 0};
sgt.init(1, 0, n); sgt2.init(1, 1, n);
solve(0, n); // F(i, 0, n) cout << i << ": " << f[i].mx << ' ' << f[i].s << '\n'; if (f[n].s == 0) cout << "NIE" << endl;
else cout << f[n].mx << ' ' << f[n].s << endl; return 0;
}

洛谷 P5979 [PA2014] Druzyny的更多相关文章

  1. P5979 [PA2014]Druzyny dp 分治 线段树 分类讨论 启发式合并

    LINK:Druzyny 这题研究了一下午 终于搞懂了. \(n^2\)的dp很容易得到. 考虑优化.又有大于的限制又有小于的限制这个非常难处理. 不过可以得到在限制人数上界的情况下能转移到的最远端点 ...

  2. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  3. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

  4. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

  5. 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  6. 洛谷P1710 地铁涨价

    P1710 地铁涨价 51通过 339提交 题目提供者洛谷OnlineJudge 标签O2优化云端评测2 难度提高+/省选- 提交  讨论  题解 最新讨论 求教:为什么只有40分 数组大小一定要开够 ...

  7. 洛谷P1371 NOI元丹

    P1371 NOI元丹 71通过 394提交 题目提供者洛谷OnlineJudge 标签云端评测 难度普及/提高- 提交  讨论  题解 最新讨论 我觉得不需要讨论O long long 不够 没有取 ...

  8. 洛谷P1538迎春舞会之数字舞蹈

    题目背景 HNSDFZ的同学们为了庆祝春节,准备排练一场舞会. 题目描述 在越来越讲究合作的时代,人们注意的更多的不是个人物的舞姿,而是集体的排列. 为了配合每年的倒计时,同学们决定排出——“数字舞蹈 ...

  9. 洛谷八月月赛Round1凄惨记

    个人背景: 上午9:30放学,然后因为学校举办读书工程跟同学去书城选书,中午回来开始打比赛,下午又回老家,中间抽出一点时间调代码,回家已经8:50了 也许是7月月赛时“连蒙带骗”AK的太幸运然而因同学 ...

  10. 洛谷 P1379 八数码难题 Label:判重&&bfs

    特别声明:紫书上抄来的代码,详见P198 题目描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给 ...

随机推荐

  1. Java19新特性

    本文已经收录到Github仓库,该仓库包含计算机基础.Java基础.多线程.JVM.数据库.Redis.Spring.Mybatis.SpringMVC.SpringBoot.分布式.微服务.设计模式 ...

  2. Barplot/pie/boxplot作图详解——R语言

    当数据以简单的可视化的形式呈现时,数据便更具有意义并且更容易理解,因为人眼很难从原始数据中得出重要的信息.因此,数据可视化成为了解读数据最重要的方式之一.条形图和箱线图是了解变量分布的最常用的图形工具 ...

  3. 有关Spring的ioc理解之代理模式

    AOP代理模式可以实现事务控制和业务逻辑代码横切. 使用代理模式,动态代理实现横切. 什么是代理? 接口就是指定要做的事情,要实现的逻辑. 代理类似于房源租房 public interface ZuF ...

  4. abp(net core)+easyui+efcore实现仓储管理系统——模块管理升级(六十)

    Abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...

  5. 全网最详细中英文ChatGPT-GPT-4示例文档-食谱智能生成从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/curl命令源代码,小白也能学)

    目录 Introduce 简介 setting 设置 Prompt 提示 Sample response 回复样本 API request 接口请求 python接口请求示例 node.js接口请求示 ...

  6. 如何在 vue3 中使用 jsx/tsx?

    我们都知道,通常情况下我们使用 vue 大多都是用的 SFC(Signle File Component)单文件组件模式,即一个组件就是一个文件,但其实 Vue 也是支持使用 JSX 来编写组件的.这 ...

  7. Linux 查看内存使用情况的几种方法

    *以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」https://mp.weixin.qq.com/s/27UaVm5_FMhCnxB88pc0QA 在运行 Linux 系 ...

  8. oss/obs对象存储上传图片,在浏览器输入地址却是下载图片。不能直接在浏览器上查看。

    1.问题oss/obs对象存储上传图片获取链接地址后,在浏览器输入地址却是下载.不能直接在浏览器上面浏览图片信息.2.解决上传文件的时候需要设置:content-type类型,需要指示浏览器这是什么类 ...

  9. Kubernetes(K8S) kubesphere 介绍

    使用 Kubeadm 部署 Kubernetes(K8S) 安装--附K8S架构图 官网地址:https://kubesphere.com.cn/ KubeSphere 是个全栈的Kubernetes ...

  10. [Pytorch框架] 2.3 神经网络简介

    文章目录 2.3 神经网络简介 概述 神经网络的表示 激活函数 为什么激活函数都是非线性的 sigmod 函数 tanh 函数 ReLU 函数 Leaky Relu 函数 深入理解前向传播和反向传播 ...