题意:
给定序列,求满足区间内存在一种珍珠正好出现 x 次的区间个数。
分析:
枚举左端点,找右端点的可行区间。
对于每一个珍珠,从头开始枚举左端点,对于每一个左端点
假设离开当前左端点到达下一个端点
l1
,此时区间内珍珠少了一个,那么左端点在
[l,l1)
之间的可行右端点范围是相同,我们直接累加到答案即可。到达
l1
时,区间内珍珠数目发生改变,此时我们要将
l
对应的右区间删除,右端点后移使得区间内重新满足有
对于每种珍珠都进行相同的处理,最终我们得到每个左区间对应的可行右区间,从头扫一遍,将扫描线长度累加起来即为答案。我们会发现其实本质就是求矩形面积并。
参照hezhu大神的代码理解和实现的。
代码:
/*************************************************************************
> File Name: 5722.cpp
> Author: jiangyuzhu
> Mail: 834138558@qq.com
> Created Time: 2016/7/18 14:59:14
************************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<algorithm>
#include<map>
using namespace std;
typedef pair<int, int>p;
typedef pair<int, p>pp;
typedef long long ll;
#define sa(n) scanf("%d", &(n))
const int maxn = 1e5 + 5, maxm = 1e6 + 5, mod = 1e9 + 7, oo = 0x3f3f3f3f;
int a[maxn], na[maxn];
struct Tree{
int l, r;
int len;
int cnt;
}tree[(maxn << 2) + 5];
vector<int>cnt[maxn];
vector<pp>v[maxn];
void build(int l, int r, int i)
{
tree[i].l = l;
tree[i].r = r;
tree[i].len = 0;
tree[i].cnt = 0;
if(l == r) return;
int mid = l + r >> 1;
build(l, mid, i << 1);
build(mid + 1, r, (i << 1)|1);
}
void push_up(int i)
{
if(tree[i].cnt) {
tree[i].len = tree[i].r + 1 - tree[i].l;
}else{
if(tree[i].l == tree[i].r) tree[i].len = 0;
else {
tree[i].len = tree[i << 1].len + tree[(i << 1) | 1].len;
}
}
}
void update(int l, int r, int i, int k)
{
if(l <= tree[i].l && r >= tree[i].r){
tree[i].cnt += k;
push_up(i);
return;
}
int mid = tree[i].l + tree[i].r >> 1;
if(r <= mid) update(l, r, i << 1,k);
else if(l > mid) update(l, r, (i << 1)|1,k);
else{
update(l, mid, i <<1, k);
update(mid + 1, r, (i<<1)|1, k);
}
push_up(i);
}
int main (void)
{
int T;sa(T);
while(T--){
int n, x;sa(n);sa(x);
for(int i = 0; i < n; i++){
sa(a[i]);
na[i] = a[i];
}
sort(na, na + n);
int nn = unique(na, na + n) - na;
for(int i = 0; i < n; i++){
a[i] = lower_bound(na, na + nn, a[i]) - na;
cnt[a[i]].push_back(i);
}
int al, ar, dl, dr;
for(int j = 0; j < n; j++){
if(cnt[j].size() < x) continue;
for(int i = 0; i + x <= cnt[j].size(); i++){
al = i?cnt[j][i - 1] + 1:0;
ar = cnt[j][i] + 1;
dl = cnt[j][i + x - 1];
dr = i + x == cnt[j].size()? n - 1:cnt[j][i + x] - 1;
v[al].push_back(pp(1, p(dl, dr)));
v[ar].push_back(pp(-1, p(dl, dr)));
}
}
build(0, n - 1, 1);
ll ans = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < v[i].size(); j++){
update(v[i][j].second.first, v[i][j].second.second, 1, v[i][j].first);
}
ans += tree[1].len;
}
printf("%I64d\n", ans);
for(int i = 0; i < n + 5; i++){
cnt[i].clear();
v[i].clear();
}
}
return 0;
}