[题解] [Code+#1]Yazid 的新生舞会
题面
题解
upd : \(cnt_i\) 代表值为 \(i\) 的个数
我们可以暴力枚举众数 \(k\)
把等于 \(k\) 的赋值成 1 , 不等于 \(k\) 的赋值成 -1
这样原序列就变成了一段折线

我们把他剖开一段一段来分析

这些蓝线的左右端点分别为, 一个值为众数的数的位置, 和它下一个值为众数的数的位置的前一个位置
为了方便, 我们定义 \(0\) , \(n + 1\) 这两个位置上的数可以当做任意一个位置
我们对于一条蓝线扯出来单独分析
设 \(sum_i\) 为折线在 \(i\) 这个点的值
只要我们找到两个点满足 \(i > j\) , 并且满足 \(sum_i > sum_j\) , 就有序列在 \([j + 1, i]\) 上的变化大于 0 , 也就是说是满足区间众数大于区间长度一半的
设它的值域为 \([l, r]\) , 暴力做法是这样的
对于 \(i \ in [l, r]\) , 将 \(\sum_{j = -\infty }^{i - 1} cnt_j\) 加入答案贡献
把 \(cnt_i\) 加一
考虑优化这个过程
\begin{aligned}
ans &= \sum_{i = l}^r\sum_{j=-\infty}^{i - 1}cnt_i\\
&= (r - l + 1) \sum_{j = -\infty}^{l - 1}cnt_i + \sum_{i = l}^{r - 1}(r - i)*cnt_i\\
&= (r - l + 1) \sum_{j = -\infty}^{l - 1}cnt_i + r * \sum_{i = l} ^ {r - 1}cnt_i - \sum_{i = l} ^ {r - 1}i * cnt_i
\end{aligned}
\]
所以我们在线段树上维护 \(cnt_i\) 和 \(i * cnt_i\) 即可
然后像上面那样每一个蓝色的线都这么分析
对于一个众数 \(k\) 它的复杂度为 \(O(mlogn)\) , \(m\) 为 \(a\) 中等于 \(k\) 的数的个数
所以总的复杂度就是 \(O(nlogn)\)
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
typedef long long ll;
const int N = 500005;
using namespace std;
int n, m, a[N];
struct Tree { ll sum[2], tag; } t[N << 4];
vector <int> vec[N];
ll ans;
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
void update(int p)
{
t[p].sum[0] = t[p << 1].sum[0] + t[p << 1 | 1].sum[0];
t[p].sum[1] = t[p << 1].sum[1] + t[p << 1 | 1].sum[1];
}
void pushdown(int p, int l, int r)
{
if(t[p].tag)
{
int ls = p << 1, rs = ls | 1, mid = (l + r) >> 1;
t[ls].sum[0] += 1ll * t[p].tag * (mid - l + 1), t[ls].tag += t[p].tag;
t[ls].sum[1] += 1ll * t[p].tag * (mid + l - m) * (mid - l + 1) / 2;
t[rs].sum[0] += 1ll * t[p].tag * (r - mid), t[rs].tag += t[p].tag;
t[rs].sum[1] += 1ll * t[p].tag * (mid + r + 1 - m) * (r - mid) / 2;
t[p].tag = 0;
}
}
void modify(int p, int l, int r, int ql, int qr, int k)
{
if(l > r || ql > qr) return;
if(ql <= l && r <= qr)
{
t[p].tag += k;
t[p].sum[0] += (r - l + 1) * k;
t[p].sum[1] += 1ll * (l + r - m) * (r - l + 1) / 2 * k;
return;
}
pushdown(p, l, r);
int mid = (l + r) >> 1;
if(ql <= mid) modify(p << 1, l, mid, ql, qr, k);
if(mid < qr) modify(p << 1 | 1, mid + 1, r, ql, qr, k);
update(p);
}
ll query(int p, int l, int r, int ql, int qr, int op, int opt = 1)
{
if(l > r || ql > qr) return 0;
if(ql <= l && r <= qr)
return t[p].sum[op];
pushdown(p, l, r);
int mid = (l + r) >> 1; ll res = 0;
if(ql <= mid) res = query(p << 1, l, mid, ql, qr, op, opt);
if(mid < qr) res = res + query(p << 1 | 1, mid + 1, r, ql, qr, op, opt);
update(p);
return res;
}
int main()
{
n = read <int> (), read <int> ();
m = n << 1;
for(int i = 1; i <= n; i++)
{
a[i] = read <int> ();
vec[a[i]].push_back(i);
}
for(int i = 0; i < n; i++)
vec[i].push_back(n + 1);
for(int sz, st, ed, i = 0; i < n; i++)
{
sz = vec[i].size();
if(sz == 1) continue;
st = 0;
for(int j = 0; j < sz; j++)
{
ed = 2 * j + 1 - vec[i][j];
ans += 1ll * (st - ed + 1) * query(1, 1, m, 1, ed - 1 + n, 0)
+ st * query(1, 1, m, ed + n, st - 1 + n, 0)
- query(1, 1, m, ed + n, st - 1 + n, 1);
modify(1, 1, m, ed + n, st + n, 1);
st = ed + 1;
}
st = 0;
for(int j = 0; j < sz; j++)
{
ed = 2 * j + 1 - vec[i][j];
modify(1, 1, m, ed + n, st + n, -1);
st = ed + 1;
}
}
printf("%lld\n", ans);
return 0;
}
[题解] [Code+#1]Yazid 的新生舞会的更多相关文章
- 【线段树】【P4062】 [Code+#1]Yazid 的新生舞会
Description 给定一个长度为 \(n\) 的序列,求有多少子区间满足区间众数严格大于区间长度的一半.如果区间有多个出现次数最多且不同的数则取较小的数为众数. Limitation 对于全部的 ...
- P4062 [Code+#1]Yazid 的新生舞会
思路:分治 提交:2次 错因:数组开小 题解: 我们枚举一下众数\(x\). 设\(s[n]=\sum_{i=1}^n [a[i]==x]\) 那么对于区间\((l,r]\),有\(s[r]-s[l] ...
- luogu P4062 [Code+#1]Yazid 的新生舞会(线段树+套路)
今天原来是平安夜啊 感觉这题是道好题. 一个套路枚举权值\(x\),把权值等于\(x\)的设为1,不等于的设为-1,然后问题转化为多少个区间权值和大于. 发现并不是很好做,还有一个套路,用前缀和查分来 ...
- 洛谷 P4062 - [Code+#1]Yazid 的新生舞会 的线性做法
洛谷题面传送门 一个线性做法. \(n\log n\) 解法可以戳这里查看 首先回顾一下 \(n\log n\) 解法的过程:我们对于每一个数 \(x\),考察其出现位置,设为 \(t_1,t_2,t ...
- 洛谷 P4062 - [Code+#1]Yazid 的新生舞会(权值线段树)
题面传送门 题意: 给出一个序列 \(a\),求 \(a\) 有多少个子区间 \([l,r]\),满足这个区间中出现次数最多的数出现次数 \(>\dfrac{r-l+1}{2}\) \(1 \l ...
- 【BZOJ5110】[CodePlus2017]Yazid 的新生舞会 线段树
[BZOJ5110][CodePlus2017]Yazid 的新生舞会 Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一 ...
- [loj 6253] Yazid的新生舞会
(很久之前刷的题现在看起来十分陌生a) 题意: 给你一个长度为n的序列A,定义一个区间$[l,r]$是“新生舞会的”当且仅当该区间的众数次数严格大于$\frac{r-l+1}{2}$,求有多少子区间是 ...
- bzoj5110: [CodePlus2017]Yazid 的新生舞会
Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一个子区间[l,r] ,如果该子区间内的众数在该子区间的出现次数严格大于( ...
- 【bzoj5110】Yazid的新生舞会
这里是 $THUWC$ 选拔时间 模拟赛的时候犯 $SB$ 了,写了所有的部分分,然后直接跑过了 $4$ 个大样例(一个大样例是一个特殊情况)…… 我还以为我非常叼,部分分都写对了,于是就不管了…… ...
随机推荐
- 存储过程、插入数据后直接过去主键id
DECLARE @sql nvarchar() DECLARE @cou int SET @sql='INSERT INTO people values('''+'xiaohong'+''');sel ...
- RAII Theory && auto_ptr
RAII(Resource Acquisition is Initialization),也称为"资源获取即初始化",是C++语言的一种管理资源,避免泄露的惯用法. C++标准保证 ...
- Axure工作区间
Axure的工作环境可进行可视化拖拉操作,可轻松快速的创建带有注释的线框图.无需编程就可以在线框图中定义简单链接和高级交互.Axure可一体化生成线框图.HTML交互原型.规格说明Word文档.以下是 ...
- hive 存储格式对比
Apache Hive支持Apache Hadoop中使用的几种熟悉的文件格式,如TextFile,RCFile,SequenceFile,AVRO,ORC和Parquet格式. Cloudera I ...
- LVS、nginx、Haproxy对比(详细)
目录 代理软件 负载均衡产品介绍 haproxy 本文档参考 http://www.ha97.com/5646.html 代理软件 负载均衡产品介绍 市面上的负载均衡产品主要分为两种:硬件产品和软件产 ...
- 记录一次OOM排查经历
我是用了netty搭建了一个UDP接收日志,堆启动配置 Xmx256 Xms256 ,项目刚启动的时候,系统进程占用内存很正常,在250M左右. 长时间运行之后发现,进程占用内存不断增长,远远超过了 ...
- 【问题】Debian安装、配置sources.list、安装VMware Tools
Debian安装: 我采用的是纯命令行安装方式.具体安装过程网上一大堆,不介绍了.需要强调一点,那个SSH Server必须选,否则像XShell这样的客户端不能访问Debian. 配置sources ...
- springmvc,hibernate整合时候出现Cannot load JDBC driver class 'com.mysql.jdbc.Driver
原因:不清楚是什么原因,哪位知道可以给我留言,不胜感激! 解决方法: 1.把mysql的驱动包放到你项目的WEB-INF目录下的lib目录中2.要mysql的驱动包放在tomcat/lib目录下
- Linux 下DNS详解
配置之前先了解一下bind DNS服务器软件:BIND是一种开源的DNS(Domain Name System)协议的实现,包含对域名的查询和响应所需的所有软件.它是互联网上最广泛使用的一种DNS服务 ...
- windows7重置网卡命令
点击windows左下角菜单键,输入cmd 然后鼠标右键 cmd 以管理员身份运行,并输入命令 netsh winsock reset, 然后回车执行. 系统会提示要求重启计算机. 重启后即重置网卡成 ...