题意
自己看吧,不写了
题解
这题还想了我挺久。。
考虑分块。
f[i][j]表示—–>i是块,j是位置
l1=L[i],r1=R[i];
l2=1,r2=j的答案
这个是可以扫一次就预处理出来。。
然后就可以弄了。。
在块里面的就这么弄,多余的就用一个线段树来查询某一段区间有多少个值为x的数就好
具体看代码吧,有少量注释,应该看得懂
#include<cstdio>
#include<cmath>
#include<cstring>
typedef long long LL;
const int N=50005;
const int NN=225;
int n,nn;
int a[N];
int son[N*30][2];
LL d[N*30];
int root[N];
int num;
void add (int &now,int l,int r,int x)
{
if (now==0) now=++num;
d[now]++;
if (l==r) return ;
int mid=(l+r)>>1;
if (x<=mid) add(son[now][0],l,mid,x);
else add(son[now][1],mid+1,r,x);
}
int cnt,L[N],R[N],belong[N];
void prepare()
{
L[1]=1;belong[1]=1;cnt=1;
for (int u=1;u<=n;u++)
{
belong[u]=cnt;
if (u%nn==0)
{
R[cnt]=u;
cnt++;
L[cnt]=u+1;
}
}
cnt=belong[n];R[cnt]=n;
}
LL f[NN][N];//第i块和1~i的答案
LL X[N];//这个数字有多少个,预处理用
void init ()
{
cnt=num=0;
scanf("%d",&n);nn=sqrt(n);
for (int u=1;u<=n;u++)
{
scanf("%d",&a[u]);
add(root[a[u]],1,n,u);
}
}
void prepare1 ()//预处理f数组
{
memset(X,0,sizeof(X));
for (int u=1;u<=cnt;u++)//枚举每一块
{
for (int i=L[u];i<=R[u];i++)
X[a[i]]++;
for (int i=1;i<=n;i++)
f[u][i]=f[u][i-1]+X[a[i]];
for (int i=L[u];i<=R[u];i++)
X[a[i]]--;
}
}
LL get (int now,int l,int r,int L,int R)
{
if (now==0) return 0;
if (l==L&&r==R) return d[now];
int mid=(l+r)>>1;
if (R<=mid) return get(son[now][0],l,mid,L,R);
else if (L>mid) return get(son[now][1],mid+1,r,L,R);
else return get(son[now][0],l,mid,L,mid)+get(son[now][1],mid+1,r,mid+1,R);
}
void solve ()
{
int Q;
scanf("%d",&Q);
while (Q--)
{
int l,r,l1,r1;
scanf("%d%d%d%d",&l,&r,&l1,&r1);
//printf("YES%d %d\n",belong[l],belong[r]);
LL ans=0;
if (belong[l]==belong[r])//在同一块以内
{
for (int u=l;u<=r;u++)
ans=ans+get(root[a[u]],1,n,l1,r1);
}
else
{
for (int u=belong[l]+1;u<belong[r];u++)
ans=ans+f[u][r1]-f[u][l1-1];
for (int u=l;u<=R[belong[l]];u++)
ans=ans+get(root[a[u]],1,n,l1,r1);
for (int u=L[belong[r]];u<=r;u++)
ans=ans+get(root[a[u]],1,n,l1,r1);
}
printf("%lld\n",ans);
}
}
int main()
{
init();
prepare();
prepare1();
solve();
return 0;
}