JZOJ 5428 查询

查询

Description

给出一个长度为 n 的序列a给出 q 组询问,每组询问形如(x, y ),求a序列的所有区间中,数字 x 的出现次数与数字y的出现次数相同的区间有多少个。

Data Constraint

1 <=n<= 8103 , 1 <=q<= 5105 1 <=x, y ,ai<= 109

Solution

先对所有的数离散化。
先处理掉特殊情况: x ,y中至少一者未出现在序列 a 中。
然后考虑怎么预处理询问(x, y )(x, y 两者都在序列中出现过),一个显然正确的暴力是维护一个前缀值,扫一遍,如果遇到x就加一,遇到 y 就减一,然后看前面有多少个前缀值跟当前的值相同,将个数加入答案中。

但这样做的缺点在于每次都要重新O( n )地扫一遍,所以我们可以把x的所有出现位置和 y 的所有出现位置拿出来,排序之后从前往后扫,两个相邻点之间的前缀值都一样,利用这个性质,可以O( 1 )把两个相邻点之间的答案都处理完,易证,这样预处理所有 都在a序列中的( x ,y)询问的总复杂度为 O (n2)。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>

#define fo(i,j,l) for(int i=j;i<=l;i++)
#define fd(i,j,l) for(int i=j;i>=l;i--)

using namespace std;
typedef long long ll;
const ll N=8e3+4e2,M=68e4,K=100;

struct note{
    int x,w,k;
}px[N+M*2];

int xw[M][2],n,m,j,k,l,i,o,q,a[N],num[M],b[M],e[M],d[M],r[N];
int ans[N][N],lj[2*N],bh[2*N],dq;

int read()
{
    int o=0; char ch=' ';
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar())o=o*10+ch-48;
    return o;
}

void pri(int o)
{
    if(o==0)return;else pri(o/10);
    putchar('0'+o%10);
}

void write(int o)
{
    if(o==0)putchar('0');
    else pri(o);
    putchar('\n');
}

bool ksm(note a,note b)
{return a.x<b.x;}
bool ksm2(note a,note b)
{return a.x!=b.x ? a.x<b.x : a.w<b.w ;}

int count(int x,int y)
{
    int an=0;dq++;
    int k1=b[x],k2=b[y],e1=e[x],e2=e[y];
    int f=0,g=0,last=0,now,len=e1+e2-k1-k2+2;
    fo(i,1,len){
        if((px[k1].w<px[k2].w&&k1<=e1)||k2>e2){
            now=px[k1++].w;
            if(now>last)an+=(now-last)*(now-last-1)/2;
            if(bh[f-g+n]!=dq)bh[f-g+n]=dq,lj[f-g+n]=0;
            if(now>last)an+=(now-last)*lj[f-g+n];
            lj[f-g+n]+=now-last;
            f++;
            last=now;
        }else{
            now=px[k2++].w;
            if(now>last)an+=(now-last)*(now-last-1)/2;
            if(bh[f-g+n]!=dq)bh[f-g+n]=dq,lj[f-g+n]=0;
            if(now>last)an+=(now-last)*lj[f-g+n];
            lj[f-g+n]+=now-last;
            g++;    
            last=now;
        }
    }
    if(last<=n)an+=(n-last)*(n-last+1)/2;
    if(bh[f-g+n]!=dq)bh[f-g+n]=dq,lj[f-g+n]=0;
    if(last<=n)an+=(n-last+1)*lj[f-g+n];
    return an;
}

int tj(int o)
{
    int last=0,kk=0;
    fo(i,b[o],e[o]){    
        if(last+1<px[i].w)kk+=(px[i].w-last)*(px[i].w-last-1)/2;
        last=px[i].w;
    }
    if(last<n)kk+=(n-last)*(n-last+1)/2;
    return kk;
}

int main()
{
    n=read(); q=read();
    fo(i,1,n)px[i].x=read(),px[i].w=0,px[i].k=i;
    fo(i,1,q){
        px[i*2+n-1].x=read(),px[i*2+n-1].w=1,px[i*2+n-1].k=i*2-1;
        px[i*2+n].x=read(),px[i*2+n].w=1,px[i*2+n].k=i*2;
    }
    sort(px+1,px+(n+2*q)+1,ksm);
    px[0].x=0; int kk=0;
    fo(i,1,n+2*q){
        kk+=(px[i].x!=px[i-1].x);
        if(!px[i].w)a[px[i].k]=kk;
        else xw[(px[i].k+1)/2][!(px[i].k%2>0)]=kk;
    }
    fo(i,1,n)px[i].x=a[i],px[i].w=i;
    sort(px+1,px+n+1,ksm2);
    int v=0;
    fo(i,1,n){
        if(px[i].x!=px[i-1].x)d[px[i].x]=++v,b[v]=i;
        num[a[i]]++;
        e[v]=i;
    }
    fo(i,1,v-1)fo(l,i+1,v)ans[i][l]=ans[l][i]=count(i,l);
    fo(i,1,v)r[i]=tj(i),ans[i][i]=n*(n+1)/2;
    int gd=n*(n+1)/2;
    fo(i,1,q){
        int u=xw[i][0],v=xw[i][1];
        if((num[u]|num[v])==0)write(gd);
        else if(num[u]==0)write(r[d[v]]);
        else if(num[v]==0)write(r[d[u]]);
        else write(ans[d[v]][d[u]]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值