Description
给出一个长度为n的序列a[]
给出q组询问,每组询问形如< x,y>,求a序列的所有区间中,数字x的出现次数与数字y的出现次数相同的区间有多少个
Data Constraint
对于30%的数据,1<=n<=100,1<=q<=1000
对于另外30%的数据,序列中只有最多50种不同的颜色且1<=n<=1000
对于100%的数据,1<=n<=8000,1<=q<=500000,1<=x,y,a[i]<=10^9
Solution
先讲讲我考场的做法,询问不同时为序列中的值很好做,就不讲了。显然序列中只有n种数,我们做n^2次前缀和,那么x的出现次数与y的出现次数相同的区间[i,j]满足s[x][i]-s[y][i]=s[x][j]-s[y][j]。每次询问扫一遍加个记忆化后60分。
那么下面是正解。我们遇x加1,遇y减1,那么x的出现次数与y的出现次数相同的区间[i,j]满足i上的值等于j上的值,但当枚举的数不是x或y时对值并不会产生影响,所以我们可以只用枚举值为x或y位置。加个记忆化。最坏复杂度O(N^2),证明显然。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=8e3+5,maxn1=1e6+7;
int h[maxn1],g[maxn1],a[maxn],b[maxn1],c[maxn1],d[maxn],f[maxn],bz[maxn*2][2],bz1[maxn][maxn];
int n,m,i,t,j,k,l,x,y,z,num,ans;
int hash(int x){
int t=x%maxn1;
while (h[t] && h[t]!=x) t=(t+1)%maxn1;
return t;
}
int main(){
freopen("query.in","r",stdin);freopen("query.out","w",stdout);
scanf("%d%d",&n,&m);
for (i=1;i<=n;i++){
scanf("%d",&x);
t=hash(x);
if (!h[t]) h[t]=x,g[t]=++num,f[num]=i;
c[t]+=(n-i+1)*(i-b[t]);d[b[t]]=i;
b[t]=i;a[i]=g[t];
}
for (i=1;i<=n;i++)
if (!d[i]) d[i]=n+1;
d[n+1]=n+2;
while(m--){
scanf("%d%d",&x,&y);
t=hash(x);k=hash(y);
if (!h[t] && !h[k]) t=n*(n+1)/2,printf("%d\n",t);
else if (!h[t] || !h[k]){
z=n*(n+1)/2;
if (h[t]) z-=c[t];
else z-=c[k];
printf("%d\n",z);
}else{
x=g[t];y=g[k];z=n;
if (bz1[x][y]){
printf("%d\n",bz1[x][y]);continue;
}
i=f[x];j=f[y];ans=0;
bz[n][1]=min(i,j);ans=min(i,j)*(min(i,j)-1)/2;bz[n][0]=m;
while (i<=n || j<=n){
if (i<j){
k=d[i];z++;
if (bz[z][0]!=m) bz[z][0]=m,bz[z][1]=0;
if (k>j) ans+=(bz[z][1]*2+j-i-1)*(j-i)/2,bz[z][1]+=j-i;
else ans+=(bz[z][1]*2+k-i-1)*(k-i)/2,bz[z][1]+=k-i;
i=k;
}else{
k=d[j];z--;if (bz[z][0]!=m) bz[z][0]=m,bz[z][1]=0;
if (k>i)ans+=(bz[z][1]*2+i-j-1)*(i-j)/2,bz[z][1]+=i-j;
else ans+=(bz[z][1]*2+k-j-1)*(k-j)/2,bz[z][1]+=k-j;
j=k;
}
}
printf("%d\n",ans);bz1[x][y]=ans;
}
}
}