1.算法
尺取+树状数组(nlogn)
2.实现
对于每个i来说,维护一个[l,r].然后对于每个i维护一个区间L[i],R[i]表示这个区间最大数量为k的可行区间.这里可以用个尺取就行.求出区间了之后呢,因为离线,我们可以考虑将所有区间进行排序.然后对于每个区间来说,我们可以枚举移动的点,也就是i,然后更新这个区间的答案,因为是排序完了所以必然是互不影响的,我只要控制指针移动即可得到答案.
3.代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+50;
struct vv{
int l,r,id;ll ans;
}vnt[N];
bool cmp1(vv a,vv b)
{
if(a.l==b.l) return a.r<b.r;
else return a.l<b.l;
}//按左端点优先,其次右端点排列.
bool cmp2(vv a,vv b)
{
return a.id<b.id;
}//输出答案
int n,cntl[N],cntr[N],L[N],R[N],a[N],q,k;
ll c1[N],c2[N];
ll lowbit(ll x) { return x&(-x); }
void add(ll pos,ll val)
{
ll x=pos;
while(pos<=n)
{
c1[pos]+=val;
c2[pos]+=(x-1)*val;
pos+=lowbit(pos);
}
}
ll query(ll pos)
{
ll res=0;ll x=pos;
while(pos)
{
res+=x*c1[pos]-c2[pos];
pos-=lowbit(pos);
}return res;
}
void init()//预处理出L,R数组.
{
//cout<<k<<endl;
int l=1,r=1;//设立两个指针.
for(int i=1;i<=n;i++)
{
//处理l
while(l<=n&&cntl[a[l]]+1<k) { cntl[a[l]]++,l++; }
//处理r
while(r<=n&&cntr[a[r]]+1<=k) { cntr[a[r]]++,r++; }
r--,cntr[a[r]]--,L[i]=l,R[i]=r;
cntl[a[i]]--,cntr[a[i]]--;
}
}
void BUG()
{
for(int i=1;i<=n;i++) cout<<L[i]<<' '<<R[i]<<endl;
/*
5 3 2 n q k
1 2 2 1 2
5 5
5 4
6 5
6 5
6 4
*/
}
int main()
{
scanf("%d%d%d",&n,&q,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=q;i++)
scanf("%d%d",&vnt[i].l,&vnt[i].r),vnt[i].id=i;
sort(vnt+1,vnt+1+q,cmp1);
init();
//BUG();
int l=1,r=0;
for(int pos=1;pos<=q;pos++)//枚举区间.
{
//(L[i],R[i]) || (vnt[i].l,vnt[i].r)
while(l<vnt[pos].l) add(L[l],-1),add(R[l]+1,1),l++;
while(r<vnt[pos].r) add(L[++r],1),add(R[r]+1,-1);
vnt[pos].ans=query(vnt[pos].r)-query(vnt[pos].l-1);
}
sort(vnt+1,vnt+1+q,cmp2);
for(int i=1;i<=q;i++) printf("%lld\n",vnt[i].ans);
return 0;
}
/*
模拟你都不会好意思打算法竞赛?
*/

京公网安备 11010502036488号