​传送门​

考虑以每个 i i i为左端点,预处理右端点的合法区间为 [ l i , r i ] [l_i,r_i] [li,ri]

预处理的话只需要尺取就好了,因为处理 l i l_i li只需要处理到刚好有数等于 k k k次

处理 r i r_i ri就是刚好有数等于 k + 1 k+1 k+1次,此时下标减去一即可

那么现在离线询问,按照左端点大到小排序

一开始 p o s = n pos=n pos=n

然后每次让 p o s pos pos左移动到当前询问左端点的位置

每移动到一个位置,就把那个点的合法右端点 [ l i , r i ] [l_i,r_i] [li,ri]加入线段树

然后查询的时候直接查询贡献就好了

查询询问 [ l , r ] [l,r] [l,r]的时候,在线段树上 [ l , r ] [l,r] [l,r]上的产生所有贡献的左端点都大于 l l l

所以不会多算

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r
const int maxn = 2e6+10;
struct p{
int l,r,pos;
bool operator < (const p&tmp ) const{ return l>tmp.l; }
}a[maxn];
int L[maxn],R[maxn],w[maxn],laz[maxn],ans[maxn],n,m,k,c[maxn];
void pushdown(int rt,int l,int r)
{
if( laz[rt]==0 ) return;
w[ls] += laz[rt]*(mid-l+1); w[rs] += laz[rt]*(r-mid);
laz[ls] += laz[rt]; laz[rs] += laz[rt];
laz[rt] = 0;
}
void update(int rt,int l,int r,int L,int R,int val)
{
if( l>=L&&r<=R ){ w[rt] += val*(r-l+1); laz[rt] += val; return; }
if( l>R||r<L ) return;
pushdown(rt,l,r);
update(lson,L,R,val); update(rson,L,R,val);
w[rt] = w[ls]+w[rs];
}
int ask(int rt,int l,int r,int L,int R)
{
if( l>=L&&r<=R ) return w[rt];
if( l>R||r<L ) return 0;
pushdown(rt,l,r);
return ask(lson,L,R)+ask(rson,L,R);
}
//尺取部分
int sum = 0,vis[maxn];
void add(int index,int lim)
{
if( vis[c[index]]==lim-1 ) sum++;
vis[c[index]]++;
}
void del(int index,int lim)
{
if( vis[c[index]]==lim ) sum--;
vis[c[index]]--;
}
void ruler()
{
for(int l=1,r=1;l<=n;l++)
{
while( r<=l ) add(r++,k);
while( r<=n&&sum<1 ) add(r++,k);
if( sum ) L[l] = r-1;
del(l,k);
}
for(int i=0;i<=n;i++) vis[i] = 0; sum = 0;
for(int l=1,r=1;l<=n;l++)
{
while( r<=l ) add(r++,k+1);
while( r<=n&&sum<1 ) add(r++,k+1);
if( sum ) R[l] = r-2;
else R[l] = n;
del(l,k+1);
}
}
signed main()
{
cin >> n >> m >> k;
for(int i=1;i<=n;i++) scanf("%lld",&c[i] );
ruler();
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&a[i].l,&a[i].r);
a[i].pos = i;
}
sort( a+1,a+1+m );
int x = n;
for(int i=1;i<=m;i++)
{
while( x>=a[i].l )
{
if( L[x]!=0 )
update(1,1,n,L[x],R[x],1);
x--;
}
ans[a[i].pos] = ask(1,1,n,a[i].l,a[i].r);
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i] );
}