【线段树】【P4062】 [Code+#1]Yazid 的新生舞会
Description
给定一个长度为 \(n\) 的序列,求有多少子区间满足区间众数严格大于区间长度的一半。如果区间有多个出现次数最多且不同的数则取较小的数为众数。
Limitation
对于全部的数据,\(1 \leq n \leq 500000\)
序列中数的值域为 \([0,n)\)
子任务:序列中的数值域为 \([0,7]\)
Solution
考虑如果区间有多个出现次数最多且不同的数,那么这个区间显然是不合法的。于是区间出现多个众数取最小的限制其实没有什么 * 用。
考虑枚举区间众数是 \(x\),将区间中等于 \(x\) 的数置为 \(1\),不等于的置为 \(-1\),考虑 \(x\) 是该区间的众数且出现次数严格大于区间长度一半的充要条件是区间的和大于 \(0\)。
于是考虑问题被转化成了给定一个仅含有 \(-1\) 和 \(1\) 的序列,求有多少子区间满足区间和大于 \(0\)。
对于序列的子区间问题的一个经典套路是通过做前缀和转化成序列点对问题,这是因为子区间有 \(O(n^2)\) 个,而点只有 \(O(n)\) 个,子区间甚至遍历一遍复杂度都难以支持,但是单点只需要在遍历时维护对应信息即可。
考虑对序列做前缀和,那么 \([l,~r]\) 区间是合法的当且仅当 \(sum_r - sum_{l - 1} > 0\),也即 \(sum_r > sum_{l - 1}\)。于是只需要求前缀和序列的顺序对个数即可。
对于序列值域较小的子任务,可以直接枚举众数,然后每次用权值线段树/权值树状数组 \(O(n \log n)\) 去求顺序对个数。时间复杂度 \(O(\max_{i = 1}^{n}\{A_i\} \times n \log n)\)。
考虑枚举这 \(O(n)\) 个众数一共产生 \(O(n)\) 个 \(1,~-1\) 序列,共有 \(O(n^2)\) 个数,在这 \(O(n^2)\) 个数中只有 \(O(n)\) 个 \(1\),剩下全是 \(-1\)。由于可以认为是这 \(O(n)\) 个 \(1\) 和 \(O(n)\) 个空白(即两个相邻序列)将这 \(O(n^2)\) 个 \(-1\) 隔开,于是连续的 \(-1\) 序列有 \(O(n)\) 个。
考虑每段连续的 \(-1\) 序列,假设它们的前缀和值域是 \([l,~r]\),则它们对答案产生的贡献是
\]
其中 \(tree_i\) 为 \(i\) 出现的次数。
于是考虑只需要用权值线段树维护 \(tree_i\) 和 \(tree_i \times i\) 即可。考虑将这连续一段 \(-1\) 插入可以区间 \(+1\)。对于不是 \(-1\) 的位置,显然可以单次 \(O(\log n)\) 计算。于是总复杂度 \(O(n \log n)\)。
Code
#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define printf(...)
typedef long long int ll;
namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
}
template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
}
namespace OPT {
char buf[120];
}
template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
}
const int maxn = 500005;
int n;
ll ans;
int MU[maxn];
std::vector<int>num[maxn];
struct Tree {
int l, r;
ll sum;
Tree *ls, *rs;
std::pair<ll, ll> v;
std::pair<int, bool> tag;
Tree(const int _l, const int _r) : l(_l), r(_r), sum(((r - l + 1ll) * (r + l)) >> 1), ls(NULL), rs(NULL) {}
inline void maketag(std::pair<int, bool> v) {
if (v.second) {
this->v.first = this->v.second = this->tag.first = 0;
this->tag.second = true;
}
this->v.first += (r - l + 1ll) * v.first;
this->v.second += v.first * sum;
this->tag.first += v.first;
}
inline void pushdown() {
this->ls->maketag(this->tag);
this->rs->maketag(this->tag);
this->tag.first = this->tag.second = 0;
}
inline void pushup() {
this->v.first = this->ls->v.first + this->rs->v.first;
this->v.second = this->ls->v.second + this->rs->v.second;
}
inline bool InRange(const int l, const int r) {
return (this->l >= l) && (this->r <= r);
}
inline bool OutofRange(const int l, const int r) {
return (this->l > r) || (this->r < l);
}
};
Tree *rot;
void build(Tree *const u);
int calc(const int k, const int p);
void update(Tree *const u, const int l, const int r);
std::pair<ll, ll> query(Tree *const u, const int l, const int r);
int main() {
freopen("1.in", "r", stdin);
qr(n); qr(ans); ans = 0;
for (int i = 1; i <= n; ++i) {
qr(MU[i]); num[MU[i]].push_back(i);
}
build(rot = new Tree(-n, n));
for (int k = 0; k < n; ++k) {
rot->maketag(std::make_pair(0, true));
int l = 0, cnt = 0;
for (auto u : num[k]) {
if (u != l) {
int r = u - 1;
int ar = calc(cnt, l), al = calc(cnt, r);
auto ret1 = query(rot, -n, al - 1), ret2 = query(rot, al, ar);
ans += ret1.first * (ar - al + 1) + ret2.first * ar - ret2.second;
update(rot, al, ar);
printf("QWAQ%d %d %lld\n", k, u, ans);
}
int v = calc(++cnt, u);
ans += query(rot, -n, v - 1).first;
printf("QWAQ%d %d %lld\n", k, u, ans);
update(rot, v, v);
l = u + 1;
}
if (l <= n) {
int r = n;
int ar = calc(cnt, l), al = calc(cnt, r);
auto ret1 = query(rot, -n, al - 1), ret2 = query(rot, al, ar);
printf("EMM%lld %lld %lld %lld\n", ret1.first, ret1.second, ret2.first, ret2.second);
ans += ret1.first * (ar - al + 1) + ret2.first * ar - ret2.second;
}
printf("QWQ%d %lld\n", k, ans);
}
qw(ans, '\n', true);
return 0;
}
void update(Tree *const u, const int l, const int r) {
if (u->InRange(l, r)) {
u->maketag(std::make_pair(1, false));
} else if (!u->OutofRange(l, r)) {
u->pushdown();
update(u->ls, l, r); update(u->rs, l, r);
u->pushup();
}
printf("UPD%d %d %d %d %d %d\n", u->l, u->r, l, r, u->v.first, u->v.second);
}
std::pair<ll, ll> query(Tree *const u, const int l, const int r) {
printf("EMMQAQ%d %d %d %d %d\n", u->l, u->r, l, r, u->v.first);
if (u->InRange(l, r)) {
return u->v;
} else if (u->OutofRange(l, r)) {
return std::make_pair(0ll, 0ll);
} else {
u->pushdown();
auto rl = query(u->ls, l, r), rr = query(u->rs, l, r);
return std::make_pair(rl.first + rr.first, rl.second + rr.second);
u->pushup();
}
}
inline int calc(const int k, const int p) {
return (k << 1) - p;
}
void build(Tree *const u) {
if (u->l == u->r) return;
int mid = (u->l + u->r) >> 1;
build(u->ls = new Tree(u->l, mid)); build(u->rs = new Tree(mid + 1, u->r));
}
Summary
对于子区间问题,可以通过做前缀和转化成点对问题。
【线段树】【P4062】 [Code+#1]Yazid 的新生舞会的更多相关文章
- luogu P4062 [Code+#1]Yazid 的新生舞会(线段树+套路)
今天原来是平安夜啊 感觉这题是道好题. 一个套路枚举权值\(x\),把权值等于\(x\)的设为1,不等于的设为-1,然后问题转化为多少个区间权值和大于. 发现并不是很好做,还有一个套路,用前缀和查分来 ...
- 洛谷 P4062 - [Code+#1]Yazid 的新生舞会(权值线段树)
题面传送门 题意: 给出一个序列 \(a\),求 \(a\) 有多少个子区间 \([l,r]\),满足这个区间中出现次数最多的数出现次数 \(>\dfrac{r-l+1}{2}\) \(1 \l ...
- 洛谷 P4062 - [Code+#1]Yazid 的新生舞会 的线性做法
洛谷题面传送门 一个线性做法. \(n\log n\) 解法可以戳这里查看 首先回顾一下 \(n\log n\) 解法的过程:我们对于每一个数 \(x\),考察其出现位置,设为 \(t_1,t_2,t ...
- P4062 [Code+#1]Yazid 的新生舞会
思路:分治 提交:2次 错因:数组开小 题解: 我们枚举一下众数\(x\). 设\(s[n]=\sum_{i=1}^n [a[i]==x]\) 那么对于区间\((l,r]\),有\(s[r]-s[l] ...
- [题解] [Code+#1]Yazid 的新生舞会
题面 题解 upd : \(cnt_i\) 代表值为 \(i\) 的个数 我们可以暴力枚举众数 \(k\) 把等于 \(k\) 的赋值成 1 , 不等于 \(k\) 的赋值成 -1 这样原序列就变成了 ...
- 【BZOJ5110】[CodePlus2017]Yazid 的新生舞会 线段树
[BZOJ5110][CodePlus2017]Yazid 的新生舞会 Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一 ...
- BZOJ.5110.[CodePlus2017]Yazid 的新生舞会(线段树/树状数组/分治)
LOJ BZOJ 洛谷 又来发良心题解啦 \(Description\) 给定一个序列\(A_i\).求有多少个子区间,满足该区间众数出现次数大于区间长度的一半. \(n\leq5\times10^5 ...
- 「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)
学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2 ...
- [loj 6253] Yazid的新生舞会
(很久之前刷的题现在看起来十分陌生a) 题意: 给你一个长度为n的序列A,定义一个区间$[l,r]$是“新生舞会的”当且仅当该区间的众数次数严格大于$\frac{r-l+1}{2}$,求有多少子区间是 ...
随机推荐
- 从URL重写中学习正则表达式
起因: 最近因为业务上的需求,老板要求改写网站的域名.要求把所有的二级域名中的内容放到主域名下,增加资源集中程度,有利于搜索引擎的优化. so.网站中所有的URL定向都要重写,也就是我们所说的伪静态的 ...
- arcgis js api 4.X 自定义工具按钮
// All material copyright ESRI, All Rights Reserved, unless otherwise specified. // See https://js.a ...
- Spring Boot 中 10 行代码构建 RESTful 风格应用
RESTful ,到现在相信已经没人不知道这个东西了吧!关于 RESTful 的概念,我这里就不做过多介绍了,传统的 Struts 对 RESTful 支持不够友好 ,但是 SpringMVC 对于 ...
- python调用MySQL数据库
在Python中访问mysql数据库中的数据需要三步骤: 1,建立连接 2,操作数据库 3,连接关闭
- SSM基本配置详解
需要查看SSM基本依赖和完整配置文件的到:SSM基本配置及依赖 示例项目:SSMDemo 1 Spring IOC容器配置 1.1 applicationContext.xml 1.1.1 配置数据源 ...
- 【题解】Diferenc-Diferencija [SP10622]
[题解]Diferenc-Diferencija [SP10622] 传送门:\(\text{Diferenc-Diferencija}\) \(\text{[SP10622]}\) [题目描述] 序 ...
- C# vb .NET读取识别条形码线性条码UPC-E
UPC-E是比较常见的条形码编码规则类型的一种.如何在C#,vb等.NET平台语言里实现快速准确读取该类型条形码呢?答案是使用SharpBarcode! SharpBarcode是C#快速高效.准确的 ...
- sedlauncher.exe 磁盘爆满
打开应用和功能,搜KB4023057,然后卸载. 快捷键WIN+R打开运行,输入services.msc回车打开系统服务,找到Windows Remediation Service (sedsvc)和 ...
- 15-213 Bomb Lab
bomb lab是一道学习汇编语言的题,一共有六个阶段,全部解开即可完成 phase_1 0x0000000000400e32 <+>: callq 0x40149e <read_l ...
- uni-app 无痛刷新 token 方法
为了给用户一个流畅的体验, token 过期后需要重新请求新的 token 替换过期的 token 前端在请求接口时,和后端定义好了,如果状态码为 401 ,则表明 token 过期,需要前端请求新的 ...