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

55 篇文章 0 订阅

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值