【JZOJ5428】【NOIP2017提高A组集训10.27】查询

来源:互联网 发布:请假流程数据库设计 编辑:程序博客网 时间:2024/06/05 18:59

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;        }    }}
阅读全文
1 0
原创粉丝点击