听说有个经典题叫二维偏序统计,即求坐标平面上给定矩形内的点数。
可以通过二位前缀和转化为求一个点左下角的点数。
再换个角度,求一个数列中每个数之前小于它的数的个数,可以用树状数组轻松处理。
同样的,可以将平面上的点与矩形的端点排序,当枚举到前者就执行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;
}