题意:
求出区间内含有x个【任意一种】珠子的区间个数。
POINT:
处理出每种珠子的位置。
若a和b珠子之内(包含他们两个)共有x个珠子。
那么对答案的贡献就是pre[a](前一颗珠子)+1到a 乘上 b到next[b]-1。
即[pre[a]+1,a] *[b,next[b]-1]。
那么如何去重, 用线段树求矩形公共面积的方法做。
#include <map>
#include <queue>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
const LL N = 1001010;
#define lt x<<1
#define rt x<<1|1
vector<int>pos[N];
map<int,int>mp;
int n,x,lcnt,dcnt;
int sum[N];
int flag[N];
struct node
{
int l,r,h,k;
friend bool operator < (node a,node b)
{
return a.h<b.h;
}
}len[100*N];
void add(int l,int r,int h,int k)
{
len[++lcnt].l=l;
len[lcnt].r=r;
len[lcnt].h=h;
len[lcnt].k=k;
}
void init()
{
memset(sum,0,sizeof sum);
memset(flag,0,sizeof(flag));
lcnt=dcnt=0;
mp.clear();
for(int i=1;i<=n;i++)
pos[i].clear();
}
void add(int x,int l,int r,int ll,int rr,int k)
{
if(ll<=l&&r<=rr){
flag[x]+=k;
}else{
int mid = (l+r)>>1;
if(ll<=mid) add(lt,l,mid,ll,rr,k);
if(mid<rr) add(rt,mid+1,r,ll,rr,k);
}
if(flag[x]){
sum[x]=r-l+1;
}else
sum[x]=sum[rt]+sum[lt];
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&x);
init();
for(int i=1;i<=n;i++){
int a;
scanf("%d",&a);
a=mp[a]?mp[a]:mp[a]=++dcnt;
pos[a].push_back(i);
}
for(int i=1;i<=dcnt;i++){
int to=pos[i].size()-x;
for(int j=0;j<=to;j++){
int l,r,h1,h2;
l=j?pos[i][j-1]+1:1;
r=pos[i][j];
h1=pos[i][j+x-1];
h2=(j+x==pos[i].size())?n:pos[i][j+x]-1;
add(l,r,h1,1);
add(l,r,h2+1,-1);
}
}
sort(len+1,len+1+lcnt);
// for(int i=1;i<=lcnt;i++){
// printf("%d %d %d %d \n",len[i].l,len[i].r,len[i].h,len[i].k);
// }
LL ans=0;
for(int i=1;i<=lcnt;i++){
ans+=(LL)(sum[1]*1LL*(len[i].h-len[i-1].h));
add(1,1,n,len[i].l,len[i].r,len[i].k);
}
printf("%lld\n",ans);
}
}