CF666E Forensic Examination 题解

发布时间 2023-08-30 22:11:05作者: trh0630

一、题目描述:

  给你一个长度为 $n$ 模板串 $S$ 以及 $m$ 个匹配串 $T$。

  $q$ 次询问,给定 $l,r,L,R$,询问 $S_l\sim S_r$ 在 $T_L\sim T_R$ 中出现次数最多的字符串编号以及最多的出现次数。

  注意,若有多个出现次数最多的字符串,取编号最小的那一个。

  数据范围:$1\le n,q\le 5\times 10^5,1\le m\le 5\times 10^4,1\le \sum_{i=1}^m len_{T_i}\le 5e4$


 二、解题思路:

  只会 $SA$。

  然后就是 $height$ 数组上求出现次数最多的 $T$,这是明显的回滚莫队。

  但是这题求得是区间最大值,所以再加一个分块记录区间答案。

  怎么样,说起来简单吧?巨大难写!巨大难调!时间复杂度 $O(nlog_2^n+n\sqrt n)$,常数极大。


 三、完整代码:

  1 #include<bits/stdc++.h>
  2 #define N 1000010
  3 #define rep(i,l,r) for(int i=l;i<=r;i++)
  4 #define per(i,r,l) for(int i=r;i>=l;i--)
  5 using namespace std;
  6 int n,m,num,p=122;
  7 int base[20],st[N][20],val[N];
  8 int s[N],h[N],tp[N],sa[N],rak[N];
  9 int c[N],mx[N],tag[N],ans[N],cnt[N];
 10 int k[N],t[N],T[N],head,tail,sn;
 11 struct ST{
 12     int l,r,id;
 13     bool operator != (const ST &t) const{
 14         return (l!=t.l||r!=t.r);
 15     }
 16 }a[N],b[N];
 17 struct Query{
 18     int l,r,L,R,id;
 19     bool operator < (const Query &t) const{
 20         if(k[l]!=k[t.l]) return l<t.l;
 21         else return r<t.r;
 22     }
 23 }q[N];
 24 void qsort(){
 25     rep(i,0,m) tp[i]=0;
 26     rep(i,1,n) tp[a[i].r]++;
 27     rep(i,1,m) tp[i]+=tp[i-1];
 28     per(i,n,1) b[tp[a[i].r]--]=a[i];
 29     rep(i,0,m) tp[i]=0;
 30     rep(i,1,n) tp[b[i].l]++;
 31     rep(i,1,m) tp[i]+=tp[i-1];
 32     per(i,n,1) a[tp[b[i].l]--]=b[i];
 33 }
 34 void suffixsort(){
 35     rep(i,1,n) rak[i]=s[i];
 36     for(int w=1;w<=n;w<<=1){
 37         rep(i,1,n) a[i]={rak[i],rak[i+w],i};
 38         m=p; p=0; qsort();
 39         rep(i,1,n) rak[a[i].id]=(p+=a[i]!=a[i-1]);
 40         if(p==n) break;
 41     }
 42     rep(i,1,n) sa[rak[i]]=i;
 43 }
 44 void get_height(){
 45     int k=0;
 46     rep(i,1,n){
 47         if(k) k--;
 48         int j=sa[rak[i]-1];if(!j) continue;
 49         while(s[i+k]==s[j+k]&&i+k<=n&&j+k<=n) k++;
 50         h[rak[i]]=k;
 51     }
 52 }
 53 void rebuild_ST(){
 54     base[0]=1;
 55     rep(i,1,n) st[i][0]=h[i];
 56     rep(i,1,n) val[i]=log(i)/log(2);
 57     rep(i,1,19) base[i]=base[i-1]<<1;
 58     rep(i,1,19) rep(j,1,n){
 59         st[j][i]=st[j][i-1]; int k=j+base[i-1];
 60         if(k<=n) st[j][i]=min(st[j][i],st[k][i-1]);
 61     }
 62 }
 63 int calc(int l,int r){
 64     int k=val[r-l+1];
 65     return min(st[l][k],st[r-base[k]+1][k]);
 66 }
 67 void Insert(int id){
 68     string x; cin>>x; int len=x.length();
 69     s[n+1]=++p; n+=len+1;
 70     rep(i,n-len+1,n) s[i]=x[i-n+len-1],c[i]=id;
 71 }
 72 int Find_L(int pos,int val){
 73     int l=1,r=pos,res=pos;
 74     while(l<=r){
 75         int mid=(l+r)>>1;
 76         if(calc(mid,pos)>=val) res=r=mid-1;
 77         else l=mid+1;
 78     } return res;
 79 }
 80 int Find_R(int pos,int val){
 81     int l=pos+1,r=n,res=pos;
 82     while(l<=r){
 83         int mid=(l+r)>>1;
 84         if(calc(pos+1,mid)>=val) res=mid,l=mid+1;
 85         else r=mid-1;
 86     } return res;
 87 }
 88 void upt(int id){
 89     tail=id*sn; rep(i,1,max(n,m)) t[i]=mx[i]=0;
 90 }
 91 void baoli(int id){
 92     int res=q[id].L;
 93     rep(i,q[id].l,q[id].r){
 94         int v=c[sa[i]];T[v]++;
 95         if(q[id].L<=v&&v<=q[id].R){
 96             if(T[v]>T[res]) res=v;
 97             if(T[v]==T[res]&&v<res) res=v;
 98         }
 99     }
100     ans[q[id].id]=res;cnt[q[id].id]=T[res];
101     rep(i,q[id].l,q[id].r) T[c[sa[i]]]=0;
102 }
103 void mdf(int id,int v,int pos){
104     if(v>cnt[id]) cnt[id]=v,ans[id]=pos;
105     if(v==cnt[id]&&pos<ans[id]) ans[id]=pos;
106 }
107 void add(int pos){
108     int v=c[sa[pos]]; t[v]++;
109     if(t[v]>mx[k[v]]) mx[k[v]]=t[v],tag[k[v]]=v;
110     if(t[v]==mx[k[v]]&&v<tag[k[v]]) tag[k[v]]=v;
111 }
112 void add1(int id,int pos){
113     int u=c[sa[pos]]; T[u]++;
114     if(q[id].L<=u&&u<=q[id].R) mdf(q[id].id,t[u]+T[u],u);
115 }
116 int main(){
117     ios::sync_with_stdio(false);
118     cin.tie(0);cout.tie(0);
119     string x; cin>>x; n=x.length();
120     rep(i,1,n) s[i]=x[i-1],c[i]=1e5;
121     cin>>num; rep(i,1,num) Insert(i);
122     suffixsort(); get_height(); rebuild_ST();
123     cin>>num; rep(i,1,num){
124         int _l,_r,_L,_R;
125         cin>>_L>>_R>>_l>>_r;
126         int nl=Find_L(rak[_l],_r-_l+1);
127         int nr=Find_R(rak[_l],_r-_l+1);
128         q[i]={nl,nr,_L,_R,i};
129     }
130     sn=sqrt(n); rep(i,1,n) k[i]=(i+sn-1)/sn;
131     sort(q+1,q+1+num);
132     rep(i,1,num){
133         if(k[q[i].l]!=k[q[i-1].l]) upt(k[q[i].l]);
134         if(k[q[i].l]==k[q[i].r]) {baoli(i);continue;}
135         while(tail<q[i].r) tail++,add(tail);
136         rep(j,q[i].l,k[q[i].l]*sn) add1(i,j);
137         rep(j,q[i].l,k[q[i].l]*sn) T[c[sa[j]]]=0;
138         if(k[q[i].L]==k[q[i].R]){
139             rep(j,q[i].L,q[i].R) mdf(q[i].id,t[j],j);
140             if(!cnt[q[i].id]) ans[q[i].id]=q[i].L;
141             continue;
142         }
143         int now=k[q[i].L]+1;while(q[i].R>=now*sn)
144             mdf(q[i].id,mx[now],tag[now]),now++;
145         rep(j,(now-1)*sn+1,q[i].R) mdf(q[i].id,t[j],j);
146         rep(j,q[i].L,k[q[i].L]*sn) mdf(q[i].id,t[j],j);
147         if(!cnt[q[i].id]) ans[q[i].id]=q[i].L;
148     }
149     rep(i,1,num) cout<<ans[i]<<" "<<cnt[i]<<'\n';
150     return 0;
151 }

四、写题心得:

  很难写,收获很大:

  $1、离谱,顺带把回滚莫队学了。=> Exp++!$

  $2、细节巨多,找问题的能力、码力大幅提升=> Exp++!$