P9196 [JOI Open 2016] 销售基因链

Link\text{Link}

分析

假设题目只询问一个前缀,我们知道,将一堆字符串排序后,前缀相同的串是排在一起的,所以可以直接排序,二分出第一个和最后一个前缀相同的位置即可。

进一步的,如果有多组询问,我们可以把询问的前缀 SS 直接放进那堆字符串里一起排序,这样前缀所在的位置往后一段区间的字符串的前缀都是该前缀,但我们还需要知道末尾,注意到题目中的字符串都是大写字母,所以字符串 S+aS+\texttt{a} 一定比其他前缀为 SS 的前缀大,所以也把 S+aS+\texttt{a} 加入其中一起排序,这样我们就可以求出前缀为 SS 的字符串所在的区间 [lpreS,rpreS][lpre_S,rpre_S] 了。

更进一步的,加上后缀的限制,我们把字符串反过来,也可以求出每个后缀对应的区间 [lsufS,rsufS][lsuf_S,rsuf_S],假设一个非询问串在前缀排序时的位置为 xx,后缀排序时的位置为 yy,询问就变成了求 lpreS<x<rpreSlsufS<y<rsufSlpre_S<x<rpre_S\land lsuf_S<y<rsuf_S 的点对 (x,y)(x,y) 的个数,是个经典的二维数点问题,直接离线 ++ 树状数组即可解决。

对于排序,可以直接用 sort\text{sort},设字符串总长为 S|S|,使用 sort\text{sort} 的时间复杂度其实是 O((n+m+SlogS)log(n+m))O((n+m+|S|\log|S|)\log(n+m)) ,开 O2\text{O2} 也能过,不必用 Trie\text{Trie}

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
	long long x=0,f=1;char ch=getchar();
	while(!isdigit(ch))
	{if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void write(long long x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
const int N=1e5+10;
int n,m,c[N*3],ans[N];
string s[N],p[2][N];
struct Node{
	int rank[2];
}a[N];
struct Que{
	int l[2],r[2];
}b[N];
struct Str{
	string s;
	int id;
}d[N*3];
bool cmp(Str a,Str b){
	return a.s==b.s?a.id>b.id:a.s<b.s;
}
vector<int>ad[N*3];
vector<pair<int,int> >q1[N*3];
void add(int x,int v){
	for(;x<=n+2*m;x+=x&-x)
		c[x]+=v;
}
int ask(int x){
	int ans=0;
	for(;x;x-=x&-x)
		ans+=c[x];
	return ans;
}
void reverse(string &s){
	int len=s.size();
	for(int i=0;i*2<len;i++)
		swap(s[i],s[len-i-1]);
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++){
		cin>>s[i];
		d[i].s=s[i];d[i].id=i;
	}
	for(int i=1;i<=m;i++){
		cin>>d[n+2*i-1].s>>p[1][i];
		d[n+2*i-1].id=n+i;
		d[n+2*i].s=d[n+2*i-1].s+"a";d[n+2*i].id=n+i;
	}
	sort(d+1,d+n+2*m+1,cmp);
	for(int i=1,j;i<=n+2*m;i++){
		if(d[i].id<=n)
			a[d[i].id].rank[0]=i;
		else{
			j=d[i].id-n;
			if(b[j].l[0])b[j].r[0]=i;
			else b[j].l[0]=i;
		}
	}
	for(int i=1;i<=n;i++){
		reverse(s[i]);
		d[i].s=s[i];d[i].id=i;
	}
	for(int i=1;i<=m;i++){
		reverse(p[1][i]);
		d[n+2*i-1].s=p[1][i];d[n+2*i-1].id=n+i;
		d[n+2*i].s=p[1][i]+"a";d[n+2*i].id=n+i;
	}
	sort(d+1,d+n+2*m+1,cmp);
	for(int i=1,j;i<=n+2*m;i++){
		if(d[i].id<=n)
			a[d[i].id].rank[1]=i;
		else{
			j=d[i].id-n;
			if(b[j].l[1])b[j].r[1]=i;
			else b[j].l[1]=i;
		}
	}
	for(int i=1;i<=n;i++)
		ad[a[i].rank[0]].push_back(i);
	for(int i=1;i<=m;i++){
		if(b[i].l[0]>b[i].r[0]||b[i].l[1]>b[i].r[1])continue;
		q1[b[i].l[0]-1].push_back(make_pair(i,0));
		q1[b[i].r[0]].push_back(make_pair(i,1));
	}
	for(int i=1;i<=n+2*m;i++){
		for(auto j:ad[i]){
			add(a[j].rank[1],1);
		}
		for(auto j:q1[i]){
			if(j.second==0)
				ans[j.first]+=ask(b[j.first].l[1]-1)-ask(b[j.first].r[1]);
			else
				ans[j.first]+=ask(b[j.first].r[1])-ask(b[j.first].l[1]-1);
		}
	}
	for(int i=1;i<=m;i++){
		write(ans[i]);puts("");
	}
	return 0;
}
posted @ 2023-08-02 23:59  luckydrawbox  阅读(10)  评论(0)    收藏  举报  来源