【线段树】【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}$,求有多少子区间是 ...
随机推荐
- axios源码入口以及公用方法
axios学习笔记(公用方法) 源码地址 找到入口文件 axios/lib/axios.js var utils = require('./utils'); var bind = require('. ...
- openresty 报错:lua entry thread aborted: runtime error
[1]问题现象 (1)本地openresty系统 (2)报错信息 2019/09/10 08:13:55 [error] 2385#2385: *4 lua entry thread aborted: ...
- everything 13问
[1]everything 由来? everything 是澳大利亚人David Carpenter开发的一个运行于windows系统,基于文件.文件夹名称的快速免费搜索引擎. 自从问世以来,因其占用 ...
- spring的15个经典面试题
总结Spring框架的15个经典面试题. 什么是Spring框架? Spring是一种轻量级框架,旨在提高开发人员的开发效率以及系统的可维护性. 我们一般说的Spring框架就是Spring Fram ...
- 【spring boot】【idea】100.idea新建一个spring boot项目
1.idea新创建一个项目 2.setting进入,选择自己的Maven 3.简单补充一下pom.xml <?xml version="1.0" encoding=" ...
- SpringBoot security关闭验证
SpringBoot security关闭验证 springboot2.x security关闭验证https://www.cnblogs.com/guanxiaohe/p/11738057.html ...
- 微信开放平台apk的应用签名的获取
https://open.weixin.qq.com 微信里面的应用签名相关的签名信息 1.首先生成JKS文件,放入的是包名,利用的是android studio工具生成的. 步骤:随便建立一个安卓项 ...
- beyond compare全文件夹比较,仅显示变化的文件
beyond compare是一款非常优秀的文件夹同步比较工具,赞. 非常强大的一点就是给定两个文件夹可以自动列出所有不同的文件和子文件夹,但是有一点可能很多人碰到过,也就是需要一个个点开才能重新比 ...
- js点击按钮button效果(波效果)
Material Design风格纯js按钮点击波特效 演示效果 html部分: <button data-ripple> Demo button 6 </button> cs ...
- datagrid相关
int rowID = Convert.ToInt32(dataGrid_OpenBoxScan.CurrentRowIndex.ToString());//得到所选行的行号 ...