概述
P4062 [Code+#1]Yazid 的新生舞会(线段树做法)
题意:
给你一个序列a[1…n],求存在绝对众数的子区间个数。
绝对众数指:区间中出现次数最多的那个数,出现次数严格大于区间长度的一半。
题解:
这两个博客将的很长清楚明白(尤其是第一个),我在反复看了n遍后,终于明白。题目细节很多,我再怎么写也没这两个详细,干脆直接放上链接。
Zechariah的博客
OMG_wc 的博客
关于题解中提到的三阶前缀和:
三阶前缀和公式转换:
图来自lx_tyin博客
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
const int N = 500005;
// 修改差分 来维护前缀和的前缀和
// c1 为差分d c2为d*i c3 为d*i*i
LL c1[N * 2], c2[N * 2], c3[N * 2];
LL sum(int x) {
LL res = 0;
for (int i = x; i > 0; i -= i & -i) {
res += c1[i] * (x + 2) * (x + 1) - c2[i] * (2 * x + 3) + c3[i];
}
return res / 2;
}
void add(int x, LL d, int n) {
for (int i = x; i <= n; i += i & -i) {
c1[i] += d;
c2[i] += d * x;
c3[i] += d * x * x;
}
}
int a[N];
vector<int> b[N];
int main() {
int n;
scanf("%d%*d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
b[a[i]].push_back(i);
}
const int wc = n + 1; // 偏移量,把[-n,n] 平移到 [1,2n+1]
LL ans = 0;
for (int i = 0; i < n; i++) {
b[i].push_back(n + 1);
int last = 0;
for (int j = 0; j < b[i].size(); j++) {
//j表示i的个数
int y = 2 * j - last + wc;
int x = 2 * j - (b[i][j] - 1) + wc;
// 查询 sum([1,t-1] 的权值和), 其中t在[x,y]范围内,
ans += sum(y - 1) - (x >= 3 ? sum(x - 2) : 0);
// [x,y] 这些数的权值+1
add(x, 1, 2 * n + 1);
add(y + 1, -1, 2 * n + 1);
last = b[i][j];
}
//撤销操作
last = 0;
for (int j = 0; j < b[i].size(); j++) {
int y = 2 * j - last + wc;
int x = 2 * j - (b[i][j] - 1) + wc;
add(x, -1, 2 * n + 1);
add(y + 1, 1, 2 * n + 1);
last = b[i][j];
}
}
printf("%lldn", ans);
return 0;
}
最后
以上就是积极酸奶为你收集整理的P4062 [Code+#1]Yazid 的新生舞会(线段树做法)的全部内容,希望文章能够帮你解决P4062 [Code+#1]Yazid 的新生舞会(线段树做法)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复