题面

题解

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\) 加一

考虑优化这个过程

\[\displaystyle
\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 的新生舞会的更多相关文章

  1. 【线段树】【P4062】 [Code+#1]Yazid 的新生舞会

    Description 给定一个长度为 \(n\) 的序列,求有多少子区间满足区间众数严格大于区间长度的一半.如果区间有多个出现次数最多且不同的数则取较小的数为众数. Limitation 对于全部的 ...

  2. P4062 [Code+#1]Yazid 的新生舞会

    思路:分治 提交:2次 错因:数组开小 题解: 我们枚举一下众数\(x\). 设\(s[n]=\sum_{i=1}^n [a[i]==x]\) 那么对于区间\((l,r]\),有\(s[r]-s[l] ...

  3. luogu P4062 [Code+#1]Yazid 的新生舞会(线段树+套路)

    今天原来是平安夜啊 感觉这题是道好题. 一个套路枚举权值\(x\),把权值等于\(x\)的设为1,不等于的设为-1,然后问题转化为多少个区间权值和大于. 发现并不是很好做,还有一个套路,用前缀和查分来 ...

  4. 洛谷 P4062 - [Code+#1]Yazid 的新生舞会 的线性做法

    洛谷题面传送门 一个线性做法. \(n\log n\) 解法可以戳这里查看 首先回顾一下 \(n\log n\) 解法的过程:我们对于每一个数 \(x\),考察其出现位置,设为 \(t_1,t_2,t ...

  5. 洛谷 P4062 - [Code+#1]Yazid 的新生舞会(权值线段树)

    题面传送门 题意: 给出一个序列 \(a\),求 \(a\) 有多少个子区间 \([l,r]\),满足这个区间中出现次数最多的数出现次数 \(>\dfrac{r-l+1}{2}\) \(1 \l ...

  6. 【BZOJ5110】[CodePlus2017]Yazid 的新生舞会 线段树

    [BZOJ5110][CodePlus2017]Yazid 的新生舞会 Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一 ...

  7. [loj 6253] Yazid的新生舞会

    (很久之前刷的题现在看起来十分陌生a) 题意: 给你一个长度为n的序列A,定义一个区间$[l,r]$是“新生舞会的”当且仅当该区间的众数次数严格大于$\frac{r-l+1}{2}$,求有多少子区间是 ...

  8. bzoj5110: [CodePlus2017]Yazid 的新生舞会

    Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一个子区间[l,r] ,如果该子区间内的众数在该子区间的出现次数严格大于( ...

  9. 【bzoj5110】Yazid的新生舞会

    这里是 $THUWC$ 选拔时间 模拟赛的时候犯 $SB$ 了,写了所有的部分分,然后直接跑过了 $4$ 个大样例(一个大样例是一个特殊情况)…… 我还以为我非常叼,部分分都写对了,于是就不管了…… ...

随机推荐

  1. Junit 学习1 junit的简单使用

    package junit; import java.sql.Connection; import java.sql.SQLException; import org.junit.Test; impo ...

  2. HTTP method GET is not supported by this URL

    Servlet eroor:HTTP method GET is not supported by this URL 错误提示: type: Status report message: HTTP m ...

  3. gin框架封装自己的路由 ②

    在一个项目中,我们会有很多路由,那么我们该如何更好的管理自己的路由,在多人协同的情况下可以更好的规范路由呢,我来说一下自己的做法 1.承接gin框架初识(先跑一个简单demo) ①,先创建一个cont ...

  4. python运行报错:cannot import name 'InteractiveConsole'

    ModuleNotFoundError: No module named '_pydevd_bundle.pydevd_cython' ImportError: cannot import name ...

  5. 编译 recastnavigation

      1. https://github.com/memononen/recastnavigation 下载zip并解压 2. 打开https://www.libsdl.org/download-2.0 ...

  6. canvas验证码实现

    1 <!DOCTYPE html> <html> <!-- head --> <head> <meta charset="utf-8&q ...

  7. 自定义flask转换器

    自定义flask转换器 以匹配手机号为例: # 1. 定义自己的转换器 class MobileConverter(BaseConverter): def __init__(self, url_map ...

  8. 【Zookeeper】应用场景概述

    一.数据发布与订阅(配置中心) 二.负载均衡 三.命名服务(Naming Service) 四.分布式通知/协调 五.集群管理与Master选举 六.分布式锁 七.分布式事务 一.数据发布与订阅(配置 ...

  9. Windows10关闭自动更新方法

    你在为windows10自动更新而烦恼吗?下面教你一招如何关闭自动更新

  10. 用js刷剑指offer(用两个栈实现队列)

    题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 牛客网链接 js代码 let stack1 = [] let stack2 = [] function ...