题目大意:
有一个长度为
的序列,每一个位置有一种颜色,颜色编号为1-n。
给定q个询问,求在p点的人如果要收集[l,r]种颜色的最小代价为多少?
收集i位置的颜色的代价为abs(p-i)。
思路:
考虑每一种颜色对于每一个位置需要收集它的人的贡献。
如果这种颜色出现了多次,对于紧接着出现的两个位置i,j,如果人在[i,mid(i,j)]的话,显然收集i位置会比较好,反之则收集j位置的比较好。对于上面划分的这样一段连续的人,他们的代价显然是单调+1或者-1的,所以可以用两个树状数组来维护。
对于计算了贡献的颜色,我们显然可以求出每一个人收集这些颜色的代价,所以可以把每一个人收集的颜色[l,r]拆分成两个区间[1,l-1],[1,r],我们顺次把每一种颜色的贡献算进去,然后每一个人在两个点的时候差分一下就好了。
orz zjB_shadow
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("loj6468.in","r",stdin);
freopen("loj6468.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=2e5+10;
int n,q,a[maxn],pos[maxn];
vector<int>col[maxn],l[maxn],r[maxn];
ll ans[maxn],sum1[maxn],sum2[maxn];
int lowbit(int x){return x&(-x);}
void modify(ll *sum,int p,ll x){while(p<=n)sum[p]+=x,p+=lowbit(p);}
ll query(ll *sum,int p){ll ret=0;while(p>=1)ret+=sum[p],p-=lowbit(p);return ret;}
void init(){
read(n); read(q);
REP(i,1,n)read(a[i]);
REP(i,1,n)col[a[i]].push_back(i);
int u,v;
REP(i,1,q){
read(pos[i]); read(u); read(v);
l[u].push_back(i);
r[v].push_back(i);
}
}
ll get_ans(int p){return query(sum1,p)+query(sum2,p)*p;}
void work(){
REP(i,1,n){
int las=1;
for(int sz=col[i].size()-1,j=0;j<=sz;++j){
int p=col[i][j];
modify(sum1,las,p); modify(sum1,p+1,-p);
modify(sum2,las,-1); modify(sum2,p+1,1);
if(j<sz)las=(col[i][j]+col[i][j+1])>>1;
else las=n;
modify(sum1,p,-p); modify(sum1,las+1,p);
modify(sum2,p,1); modify(sum2,las+1,-1);
las=las+1;
}
for(int sz=l[i+1].size()-1,j=0;j<=sz;++j)
ans[l[i+1][j]]-=get_ans(pos[l[i+1][j]]);
for(int sz=r[i].size()-1,j=0;j<=sz;++j)
ans[r[i][j]]+=get_ans(pos[r[i][j]]);
}
}
int main(){
File();
init();
work();
REP(i,1,q)printf("%lld\n",ans[i]);
return 0;
}