题目描述

求一个序列所有的子区间,满足区间众数的出现次数大于区间长度的一半。

输入

第一行2个用空格隔开的非负整数n,type,表示序列的长度和数据类型。数据类型的作用将在子任务中说明。
第二行n个用空格隔开的非负整数,依次为A1,A2,...,An,描述这个序列。
N<=500000,0<=Type<=3
对于所有数据,保证 0 ≤ Ai ≤ n - 1。
对于 type = 0 的数据,没有任何特殊约定。
对于 type = 1 的数据,保证 Ai ∈ {0, 1}。
对于 type = 2 的数据,保证序列 A 的众数在整个序列中的出现次数不超过 15。
对于 type = 3 的数据,保证 Ai ≤ 7。

输出

输出一行一个整数,表示答案。

样例输入

5 0
1 1 2 2 3

样例输出

10


题解

Treap

做麻烦了...

先把所有相同的数出现的位置存到一个vector里,然后考虑这个数作为众数的贡献。

考虑枚举贡献段的最后一个数的位置,这样对于某个左边的该数出现的位置,区间长度应该满足:区间长度小于2*这段中该数的个数。

如果设 x 表示右端点位置, y 表示右端点在某位置下满足条件的区间个数。那么由于区间长度一定,可以得到一个 y=-x+b 类型的直线(实际上有意义的只有第一象限的部分)。

但是这样会有一个问题:某些区间会计算重复。

由于考虑的是这段该数出现位置的贡献,因此区间不能包含其它的该数,即贡献区间的最端点不能达到上一个出现位置。

因此可以得到如下的图:

(好,以上只是理论部分...)

考虑一下我们需要做的:

我们枚举该数出现位置的右端点,并计算 [上一个出现的位置,当前位置) 区间的贡献。我们维护所有前面的这样的折线,统计所有折线在这段区间的面积和。

然后,由于多了当前位置,相当于以前的区间中选择区间的长度加了2,因此这些折线需要向右平移2个单位。

最后,需要把当前位置对应的折线维护起来。

一条折线可以看作是两条直线作差,因此我们需要做的有:添加直线、维护直线面积、支持直线集体向右平移。可以使用Treap来维护。

按照直线与x轴交点来维护,维护直线的斜率、截距、面积,以及子树中这三者的和。插入直接正常插入,整体平移则直接打标记。

统计面积的过程可以转化为前缀相减。在Treap上查询,如果其与左边的位置都小于当前位置,那么左边的答案就是面积总和,递归右子树;否则右边的贡献都是梯形面积(转化为等差数列求和),递归左子树。

然后就可以了,注意开long long。

细节贼多...

时间复杂度$O(n\log n)$

然后我考试时写的SBT,由于没有在旋转时pushdown(震惊!SBT竟然要在旋转时pushdown)导致爆0,再也不写SBT了...

附上Treap的代码:

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 500010
using namespace std;
typedef long long ll;
vector<int> v[N];
int l[N] , r[N] , rnd[N] , tot , root , a[N];
ll vp[N] , tag[N] , wk[N] , wb[N] , ws[N] , sk[N] , sb[N] , ss[N];
inline void add(int k , ll x)
{vp[k] += x;ws[k] += (2 * wb[k] - (x + 1) * wk[k]) * x / 2;ss[k] += (2 * sb[k] - (x + 1) * sk[k]) * x / 2;wb[k] -= wk[k] * x;sb[k] -= sk[k] * x;tag[k] += x;
}
inline void pushup(int k)
{sk[k] = sk[l[k]] + sk[r[k]] + wk[k];sb[k] = sb[l[k]] + sb[r[k]] + wb[k];ss[k] = ss[l[k]] + ss[r[k]] + ws[k];
}
inline void pushdown(int k)
{if(tag[k]) add(l[k] , tag[k]) , add(r[k] , tag[k]) , tag[k] = 0;
}
inline void zig(int &k)
{int t = l[k];l[k] = r[t] , r[t] = k , pushup(k) , pushup(t) , k = t;
}
inline void zag(int &k)
{int t = r[k];r[k] = l[t] , l[t] = k , pushup(k) , pushup(t) , k = t;
}
void insert(int &k , ll p , ll xk , ll xb)
{if(!k){k = ++tot , vp[k] = p , wk[k] = sk[k] = xk , wb[k] = sb[k] = xb , ws[k] = ss[k] = xb * (p + 1) / 2 , rnd[k] = rand();return;}pushdown(k);if(p < vp[k]){insert(l[k] , p , xk , xb) , pushup(k);if(rnd[l[k]] < rnd[k]) zig(k);}else{insert(r[k] , p , xk , xb) , pushup(k);if(rnd[r[k]] < rnd[k]) zag(k);}
}
ll query(int k , ll p)
{if(!k) return 0;pushdown(k);if(p < vp[k]) return (2 * (wb[k] + sb[r[k]]) + (wk[k] + sk[r[k]]) * p) * (p + 1) / 2 + query(l[k] , p);else return ws[k] + ss[l[k]] + query(r[k] , p);
}
void clear(int k)
{if(!k) return;clear(l[k]) , clear(r[k]) , l[k] = r[k] = vp[k] = tag[k] = wk[k] = wb[k] = ws[k] = sk[k] = sb[k] = ss[k] = 0;
}
int main()
{int n , i , pos;ll ans = 0;unsigned j;scanf("%d%*d" , &n);for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &a[i]) , v[++a[i]].push_back(i);for(i = 1 ; i <= n ; i ++ ) v[i].push_back(n + 1);for(i = 1 ; i <= n ; i ++ ){pos = 0;for(j = 0 ; j < v[i].size() ; j ++ ){ans += query(root , v[i][j] - 1) - query(root , pos - 1);add(root , 2);insert(root , v[i][j] + 1 , -1 , v[i][j] + 1) , insert(root , pos + 1 , 1 , -pos - 1);pos = v[i][j];}clear(root) , root = tot = 0;}printf("%lld\n" , ans);return 0;
}

转载于:https://www.cnblogs.com/GXZlegend/p/7953471.html

【bzoj5110】[CodePlus2017]Yazid 的新生舞会 Treap相关推荐

  1. 洛谷 - P4062 [Code+#1]Yazid 的新生舞会(推公式+线段树)

    题目链接:点击查看 题目大意:给出一个长度为 nnn 的序列,现在要求存在 绝对众数 的子区间个数 所谓 绝对众数,就是对于区间 [l,r][l,r][l,r] 来说,存在一个数字的出现次数 cntc ...

  2. P4062 [Code+#1]Yazid 的新生舞会 树状数组维护三阶差分

    传送门 文章目录 题意: 思路: 题意: 给你一个序列aaa,让你求有多少个子区间满足存在一个数是这个区间的绝对众数,绝对众数指该数在区间内出现的次数严格大于r−l+12\frac{r-l+1}{2} ...

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

    P4062 [Code+#1]Yazid 的新生舞会(线段树做法) 题意: 给你一个序列a[1-n]​,求存在绝对众数的子区间个数. 绝对众数指:区间中出现次数最多的那个数,出现次数严格大于区间长度的 ...

  4. P4062 [Code+#1]Yazid 的新生舞会(分治做法)

    P4062 [Code+#1]Yazid 的新生舞会 题意: 给出一个序列,求有多少个子区间满足众数的出现次数大于区间长度的一半. 出现次数大于区间长度的一般我们称之为绝对众数 题解: 分治做法 对于 ...

  5. P4062 [Code+#1]Yazid 的新生舞会(区间绝对众数+分治/树状数组维护高维前缀和)

    P4062 [Code+#1]Yazid 的新生舞会 杭电多校懂得都懂 Code1 分治 比较喜欢分治的做法,非常好写.skylee大佬题解 首先对于任何一个区间来说,由于两个端点不确定性非常难以一次 ...

  6. [SDOI2017]新生舞会

    题目描述 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴. 有个男生和个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴. Cathy收集了这些同学之间的关系,比如两个 ...

  7. [BZOJ 4819] [SDOI 2017] 新生舞会

    Description 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴. 有 \(n\) 个男生和 \(n\) 个女生参加舞会买一个男生和一个女生一起跳舞,互为舞伴. C ...

  8. [费用流]数字配对,新生舞会

    文章目录 T1:数字配对 题目 题解 CODE T2:新生舞会 题目 题解 CODE(最大费用最大流版) CODE(最小费用最大流版) T1:数字配对 题目 有 n 种数字,第 i 种数字是 ai.有 ...

  9. BZOJ 4819: [Sdoi2017]新生舞会

    二次联通门 : BZOJ 4819: [Sdoi2017]新生舞会 /*BZOJ 4819: [Sdoi2017]新生舞会二分 + 01分数规划 + 最小费用流二分出一个答案费用流检验*/ #incl ...

最新文章

  1. win7 html 设置成桌面,比比谁酷?!把我的Windows7变成3D桌面
  2. tensorflow gan 网络 示例
  3. Error:Execution failed for task ':myapp:dexDebug'. com.android.ide.common.process.ProcessExcepti
  4. System.arraycopy(src, srcPos, dest, destPos, length) 与 Arrays.copyOf(original, newLength)区别
  5. 最小生成树实验报告c语言,最小生成树(C语言, prim算法)
  6. android自定义图片+文字控件四种实现方法,Android自定义“图片+文字”控件四种实现方法之 二--------个人最推荐的一种...
  7. 【CODEVS2577】医院设置
  8. 金融资讯数据服务平台建设实践
  9. Linux下最快速共享目录的方法
  10. Linux 系统使用WordPress开启“固定链接设置”之后部分页面打不开(404)的解决办法...
  11. python将缺失值用0代替_平滑而不使用零填充缺失值
  12. Spring mvc实现ex导入导出
  13. Spring Boot入门(1)-第一个Spring Boot应用
  14. 一个简单的dos命令实现无限弹窗,卡死电脑
  15. Android 热修复框架: Sophix基本使用
  16. 学生HTML个人网页作业作品~蛋糕甜品店铺共11个页面(HTML+CSS+JavaScript)
  17. 【2022省选模拟】星际航道——网格图最小生成树、LCT
  18. A40i 平台应用笔记-华为-ME909S-4G 模块的移植应用
  19. 线段与线段交点的求解
  20. 淘宝旺旺号转userid 和 uid 的接口方法

热门文章

  1. iOS interface适配
  2. 【深入浅出WF】——持久化的过程
  3. makefile 基础(转)
  4. 在MySQL中当有多个索引时 你知道MySQL是如何选择索引的吗 ???
  5. Mr.J--jQuery效果总结
  6. 读《人月神话》有感2
  7. 杭电acm 1846 Brave Game(巴什博弈)
  8. LeetCode14最长公共前缀
  9. 进入快速通道的委托(深入理解c#)
  10. 一、Ajax跨域访问