#193. 字符串查询

听说有个经典题叫二维偏序统计,即求坐标平面上给定矩形内的点数。
可以通过二位前缀和转化为求一个点左下角的点数。
再换个角度,求一个数列中每个数之前小于它的数的个数,可以用树状数组轻松处理。
同样的,可以将平面上的点与矩形的端点排序,当枚举到前者就执行add操作,枚举到后者就执行query操作。
然后这题就可做了。考虑串按前缀、后缀分别排序后,相同前缀、后缀的串都是一段区间,然后就转化为上述的二维数点,就能把这题A掉了。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<cmath>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define db double
#define ldb long double
#define pii pair<int,int>
#define mkp make_pair
#define X first
#define Y second
const int N=50005;
int n,Q,sa1[N],sa2[N],rk1[N],rk2[N],cnt,sum[N][4],c[N];
string s1[N],s2[N],a,b;
pii qj[N][4];
struct TMP{pii p,wz;}tmp[N*5];
bool cmp1(int x,int y){return s1[x]<s1[y];}
bool cmp2(int x,int y){return s2[x]<s2[y];}
bool cmp(TMP x,TMP y){return x.p==y.p?x.wz<y.wz:x.p<y.p;}
void add(int x){
    for(;x<=n;x+=x&-x)++c[x];
}
int query(int x){
    int z=0;
    for(;x;x-=x&-x)z+=c[x];
    return z;
}
int main(){
    int i,j,l,la,ra,lb,rb;
    cin>>n>>Q;
    rep(i,1,n){
        cin>>s1[i];l=s1[i].size();
        per(j,l-1,0)s2[i]+=s1[i][j];
        sa1[i]=sa2[i]=i;
    }
    sort(sa1+1,sa1+n+1,cmp1);sort(sa2+1,sa2+n+1,cmp2);
    rep(i,1,n)rk1[sa1[i]]=i,rk2[sa2[i]]=i;
    rep(i,1,n)tmp[++cnt]=(TMP){mkp(rk1[i],rk2[i]),mkp(-1,-1)};
    sort(s1+1,s1+n+1);sort(s2+1,s2+n+1);
    s1[n+1]=s2[n+1]="{";
    rep(i,1,Q){
        cin>>a>>b;l=b.size();
        rep(j,0,l-j-1)swap(b[j],b[l-j-1]);
        la=lower_bound(s1+1,s1+n+1,a)-s1-1;
        a+='{';ra=lower_bound(s1+1,s1+n+1,a)-s1-1;
        lb=lower_bound(s2+1,s2+n+1,b)-s2-1;
        b+='{';rb=lower_bound(s2+1,s2+n+1,b)-s2-1;
        tmp[++cnt]=(TMP){qj[i][0]=mkp(ra,rb),mkp(i,0)};
        tmp[++cnt]=(TMP){qj[i][1]=mkp(ra,lb),mkp(i,1)};
        tmp[++cnt]=(TMP){qj[i][2]=mkp(la,rb),mkp(i,2)};
        tmp[++cnt]=(TMP){qj[i][3]=mkp(la,lb),mkp(i,3)};
    }
    sort(tmp+1,tmp+cnt+1,cmp);
    rep(i,1,cnt){
        if(tmp[i].wz.X==-1&&tmp[i].wz.Y==-1)add(tmp[i].p.Y);
        else sum[tmp[i].wz.X][tmp[i].wz.Y]=query(tmp[i].p.Y);
    }   rep(i,1,Q)printf("%d\n",sum[i][0]-sum[i][1]-sum[i][2]+sum[i][3]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值