传送门
文章目录
- 题意:
- 思路:
题意:
给你一个字符串sss,有qqq个询问,每次给x,yx,yx,y代表取sss的前xxx个字符和后yyy个字符拼接起来得到ttt,输出ttt在sss中出现的次数。
n,q≤2e5n,q\le2e5n,q≤2e5
思路:
考虑每个询问拼出来的串在sss中出现的位置,假设是[l,r][l,r][l,r],那么对于[1,x],[l,l+x−1][1,x],[l,l+x-1][1,x],[l,l+x−1]这两段区间一定是相等的,这也提示我们可以先预处理kmpkmpkmp数组nenene,不断暴跳ne[l+x−1]ne[l+x-1]ne[l+x−1],一直跳到ne[x]ne[x]ne[x]的位置,现在我们就有一个n2n^2n2的算法了,我们可以对于每一个位置都检查一下是否能跳到[1,x],[n−y+1,n][1,x],[n-y+1,n][1,x],[n−y+1,n]这两段区间即可。
考虑优化暴跳的过程,显然我们可以建一颗failfailfail树,比赛的时候也想到了,但是我很神奇的想反了!
不难发现,对于xxx节点的子树中的所有点,都可以跳到xxx这个点,比赛鬼使神差的想成了对于xxx到根的一个链都可以跳到xxx,其实当时跟队友商量一下还是能发现问题的,但是挂机一下午真的太难受了,打的一点斗志都没有。
那么问题就变成了有两棵树,我们要找出来两颗子树中满足如下条件的点对数量:假设在第一棵子树中的编号为xxx,第二棵为yyy,那么需要满足x+1=yx+1=yx+1=y,此时贡献加一。
所以我们对第一颗树建主席树,让后映射一下,映射到右边点的dfsdfsdfs序,这样查询的时候就可以直接查询右区间子树的dfsdfsdfs序内出现个数即可。
具体细节代码更清楚。。
//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;int n,q;
int ne1[N],ne2[N];
int in1[N],in2[N],out1[N],out2[N],idx;
int inv[N],root[N],tot;
char s[N];
vector<int>v1[N],v2[N];void dfs1(int u,int fa) {in1[u]=++idx;inv[idx]=u;for(auto x:v1[u]) {if(x==fa) continue;dfs1(x,u);}out1[u]=idx;
}void dfs2(int u,int fa) {in2[u]=++idx;for(auto x:v2[u]) {if(x==fa) continue;dfs2(x,u);}out2[u]=idx;
}struct MainTree
{int tot;struct Node{int l,r;int cnt;}tr[N*40];void init() {for(int i=0;i<=tot+10;i++) tr[i].l=tr[i].r=tr[i].cnt=0;tot=0;}void insert(int p,int &q,int l,int r,int x) {q=++tot; tr[q]=tr[p]; if(l==r) {tr[q].cnt++;return;}int mid=(l+r)>>1;if(x<=mid) insert(tr[p].l,tr[q].l,l,mid,x);else insert(tr[p].r,tr[q].r,mid+1,r,x);tr[q].cnt=tr[tr[q].l].cnt+tr[tr[q].r].cnt;}int query(int p,int q,int l,int r,int ql,int qr) {if(l>=ql&&r<=qr) return tr[q].cnt-tr[p].cnt;int mid=(l+r)>>1;int ans=0;if(ql<=mid) ans+=query(tr[p].l,tr[q].l,l,mid,ql,qr);if(qr>mid) ans+=query(tr[p].r,tr[q].r,mid+1,r,ql,qr);return ans;}
}tr;int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);int _; scanf("%d",&_);while(_--) {scanf("%d%d%s",&n,&q,s+1);ne1[1]=0;for(int i=2,j=0;i<=n;i++) {while(j&&s[i]!=s[j+1]) j=ne1[j];if(s[i]==s[j+1]) j++;ne1[i]=j;}ne2[n]=n+1;for(int i=n-1,j=n+1;i>=1;i--) {while(j<=n&&s[i]!=s[j-1]) j=ne2[j];if(s[i]==s[j-1]) j--;ne2[i]=j;}for(int i=1;i<=n;i++) {v1[ne1[i]].pb(i),v2[ne2[i]].pb(i);}dfs1(0,-1); idx=0; dfs2(n+1,-1); idx=0;for(int i=1;i<=n+1;i++) {tr.insert(root[i-1],root[i],1,n+1,in2[inv[i]+1]);}while(q--) {int x,y; scanf("%d%d",&x,&y);y=n-y+1;printf("%d\n",tr.query(root[in1[x]-1],root[out1[x]],1,n+1,in2[y],out2[y]));}for(int i=0;i<=n+1;i++) {ne1[i]=ne2[i]=0; root[i]=0;v1[i].clear(),v2[i].clear();}tr.init();}return 0;
}
/**/