题目大意:有n个珍珠排成一列,每个珍珠有属于一种类型Ai,问有多少个区间,使得至少有一种类型的珍珠个数恰好为X个?
题解:固定左端点l,每种类型的珍珠的贡献是一个区间,我们只需要求解这些区间的并即可。向右移动左端点,移动1格时,只有一个类型改变,修改相应的区间,再求并即可。
区间操作用线段树可求解。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N=1e5+10;
struct Node{
int Tag,Sum;
} T[N<<2];
int a[N],c[N],ord[N];
int n,X;
int m,Next[N],Last[N];
vector<int>mp[N];
bool cmp(int i,int j){return a[i]<a[j];}
void Insert(int p,int L,int R,int l,int r,int v)
{
if (L==l && R==r){
T[p].Tag+=v;
if (T[p].Tag)T[p].Sum=R-L+1;else
if (L==R)T[p].Sum=0;else T[p].Sum=T[p+p].Sum+T[p+p+1].Sum;
return ;
}
int mid=(L+R)>>1;
if (r<=mid)Insert(p+p,L,mid,l,r,v);else
if (mid< l)Insert(p+p+1,mid+1,R,l,r,v);else{
Insert(p+p,L,mid,l,mid,v);
Insert(p+p+1,mid+1,R,mid+1,r,v);
}
if (T[p].Tag)T[p].Sum=R-L+1;
else T[p].Sum=T[p<<1].Sum+T[p+p+1].Sum;
}
void work()
{
scanf("%d%d",&n,&X);
for (int i=1;i<=n;i++)scanf("%d",&a[i]);
for (int i=1;i<=n;i++)ord[i]=i;
sort(ord+1,ord+n+1,cmp);
m=1;mp[1].clear();c[ord[1]]=1;
for (int i=2;i<=n;i++) {
if (a[ord[i]]!=a[ord[i-1]])m++,mp[m].clear();
c[ord[i]]=m;
}
for (int i=1;i<=n;i++)a[i]=c[i];
for (int i=1;i<=n;i++)mp[a[i]].push_back(i);
for (int i=1;i<=m;i++)for (int j=mp[i].size()-1;j>=0;j--)
Next[mp[i][j]]=(j==mp[i].size()-1)?n+1:mp[i][j+1];
memset(T,0,sizeof T);
for (int i=1;i<=m;i++)if (mp[i].size()>=X)Insert(1,1,n,mp[i][X-1],Next[mp[i][X-1]]-1,1),Last[i]=mp[i][X-1];
LL Ans=0;
for (int i=1;i<=n;i++){
Ans+=T[1].Sum;
if (mp[a[i]].size()>=X && Last[a[i]]<=n){
Insert(1,1,n,Last[a[i]],Next[Last[a[i]]]-1,-1);
Last[a[i]]=Next[Last[a[i]]];
if (Last[a[i]]<=n)Insert(1,1,n,Last[a[i]],Next[Last[a[i]]]-1,1);
}
}
cout<<Ans<<endl;
}
int main()
{
//freopen("1.txt","r",stdin);
int Case;scanf("%d",&Case);
while (Case--)work();
return 0;
}