loj#2720. 「NOI2018」你的名字

链接大合集:

loj

uoj

luogu

bzoj

单纯地纪念一下写的第一份5K代码。。。/躺尸

因为ZJOI都不会所以只好写NOI的题了。。。

总之字符串题肯定一上来就拼个大字符串跑后缀数组啦!

(为了便于说明,放一下样例的sa)

 1 #sbape#sgepe
 2 #sgepe
 3 #smape#sbape#sgepe
 4 amgepe#smape#sbape#sgepe
 5 ape#sbape#sgepe
 6 ape#sgepe
 7 bamgepe#smape#sbape#sgepe
 8 bape#sgepe
 9 cbamgepe#smape#sbape#sgepe
10 e
11 e#sbape#sgepe
12 e#sgepe
13 e#smape#sbape#sgepe
14 epe
15 epe#smape#sbape#sgepe
16 gepe
17 gepe#smape#sbape#sgepe
18 mape#sbape#sgepe
19 mgepe#smape#sbape#sgepe
20 pe
21 pe#sbape#sgepe
22 pe#sgepe
23 pe#smape#sbape#sgepe
24 sbape#sgepe
25 scbamgepe#smape#sbape#sgepe
26 sgepe
27 smape#sbape#sgepe

首先,对于每个T,考虑求出其子串总数。对于T每个后缀[i...|T|],用st表求该后缀和它前面第一个T的后缀的lcp,则这个后缀的有效子串即为[i...j](j属于[i+lcp,|T|])。

(对于"sgepe"的后缀"epe",lcp(10,14)=1,因此,其有效子串为"ep","epe")

然后,我们再考虑去掉其有效子串中S[l...r]中的部分。

考虑64pts的部分分,即l=1,r=n。对于T的后缀[i...|T|],只要求出L=max{lcp( T[i...|T|] , S[j...|S|] )},我们就能算出每个后缀对答案的贡献。可以发现,这两个部分分别在区间上取min和max,即分别递增、递减,因此我们只要找到两个函数值最靠近的位置即可。

考虑100pts,我们可以离线做。按询问的l从大到小排序,每次在线段树上加入新的字符串使得线段树中的字符串为S[i...|S|](i>=l)。显然,i>r的字符串并不会对答案产生影响,于是我们就可以愉快地线段树上二分啦!(二分需要找到最近的满足递减函数小于递增函数的位置,在线段树上需要先上去再下来,总之写起来很恶心就对了。。。写了个人不人鬼不鬼的zkw线段树(好吧其实我并不怎么会zkw线段树。。。)总之这就是长达5k的原因。。。)

(啊,代码真丑。)

(不知道是不是因为大家都是sam而我是sa所以这么慢。。。)

(sa:所以自己常数大反而怪我咯?←_←)

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 #define rep(i,l,r) for(int i=l;i<=r;++i)
  5 #define per(i,r,l) for(int i=r;i>=l;--i)
  6 #define pb push_back
  7 #define gc getchar()
  8 #define pc putchar
  9 
 10 typedef pair<int,int> pii;
 11 typedef long long ll;
 12 
 13 const int N=2e6+3;
 14 
 15 int rd(){
 16     int x=0;char c=gc;
 17     while(c<'0'||c>'9') c=gc;
 18     while(c>='0'&&c<='9'){
 19         x=x*10+c-'0';
 20         c=gc;
 21     }
 22 
 23     return x;
 24 }
 25 
 26 void prt(ll x){
 27     if(x>9) prt(x/10);
 28     pc(x%10+'0');
 29 }
 30 
 31 //Q 查询字符串 l-r/r-l
 32 int L,n,m,sa[N];
 33 char s[N];
 34 
 35 namespace Sa{
 36     const int V=21;
 37     int ht[N],ar[2][N],*rk,*tp,ct[N],mx,ln,st[V+1][N],lg[N];
 38 
 39     void get_sa(){
 40         rk=ar[0];tp=ar[1];
 41 
 42         rep(i,1,n) ct[s[i]]++;
 43         rep(i,1,233) ct[i]+=ct[i-1];
 44         rep(i,1,n) sa[ct[s[i]]--]=i;
 45         rep(i,1,n){
 46             if(s[sa[i]]!=s[sa[i-1]]) ++mx;
 47             rk[sa[i]]=mx;
 48         }
 49 
 50         for(int k=1;mx<n;k<<=1){
 51             ln=0;
 52             rep(i,n-k+1,n) tp[++ln]=i;
 53             rep(i,1,n) if(sa[i]>k) tp[++ln]=sa[i]-k;
 54 
 55             rep(i,1,mx) ct[i]=0;
 56             rep(i,1,n) ct[rk[i]]++;
 57             rep(i,1,mx) ct[i]+=ct[i-1];
 58             per(i,n,1) sa[ct[rk[tp[i]]]--]=tp[i];
 59 
 60             swap(tp,rk);
 61             mx=0;
 62             rep(i,1,n){
 63                 int I=sa[i],J=sa[i-1];
 64                 if(tp[I]!=tp[J]||tp[min(I+k,n+1)]!=tp[min(J+k,n+1)]) ++mx;
 65                 rk[I]=mx;
 66             }
 67         }
 68     }
 69 
 70     void get_ht(){
 71         rep(i,2,n) lg[i]=lg[i>>1]+1;
 72 
 73         rep(i,1,n){//ht[rk[i]]
 74             if(rk[i]==1) continue;
 75             int v=ht[rk[i-1]];if(v>0) v--;
 76             int J=sa[rk[i]-1];
 77             while(s[i+v]==s[J+v]) v++;
 78             ht[rk[i]]=v;
 79         }
 80 
 81         rep(i,1,n) st[0][i]=ht[i];
 82         rep(j,1,V) rep(i,1,n)
 83             st[j][i]=min(st[j-1][i],st[j-1][min(i+(1<<(j-1)),n)]);
 84     }
 85 
 86     void init(){
 87         get_sa();
 88         get_ht();
 89     }
 90 
 91     int Q(int l,int r){
 92         if(l>r) swap(l,r);
 93         if(l==r) return L+1;
 94         if(r>n) return 0;
 95         l++;
 96         int j=lg[r-l+1];
 97         return min(st[j][l],st[j][r-(1<<j)+1]);
 98     }
 99 }
100 using Sa::Q;
101 using Sa::rk;
102 
103 namespace Tr{
104     const int M=(1<<22)-1,H=(1<<21)-1;
105     int mi[M+1],zuo[M+1],you[M+1];
106 
107     void build(){
108         rep(i,H+1,M){
109             zuo[i]=you[i]=i-H;
110             mi[i]=L+1;    
111         } 
112         per(i,H,1){
113             zuo[i]=zuo[i<<1];
114             you[i]=you[i<<1|1];
115             mi[i]=L+1;
116         }
117     }
118 
119     void cg(int x,int val){
120         x+=H;mi[x]=val;
121         while(x!=1){
122             x>>=1;
123             mi[x]=min(mi[x],val);
124         }
125     }
126 
127     int rt,ctr;
128 
129     bool ck(int miv,int pos){
130         return (rt+1)<Q(pos,ctr)+miv;
131         //即 (rt-miv+1)<Q(pos,ctr)
132         //即 递减函数小于递增函数
133     }
134 
135     int calc(int x){
136         if(x==ctr) return 0;
137         return min(max(rt-mi[x+H]+1,0),Q(ctr,x));
138     }
139 
140     int get_mi(int x,int oi){//oi=0 oi=1
141         while((x<<1)<M){
142             if(mi[x<<1|oi]<=rt) x=(x<<1)|oi;
143             else x=(x<<1)|(oi^1);
144         }
145         return x;
146     }
147 
148 
149     int Qr(int R,int C){//修改rt ctr
150         rt=R;ctr=C;
151 
152 
153         int ans=0,x,miv,pos;
154 
155         x=ctr+H;miv=L+1;
156         while(x!=1){
157             x>>=1;
158             if(x&1) continue;
159             if(ck(min(miv,mi[x|1]),you[x|1])) miv=min(mi[x|1],miv);
160             else{
161                 x|=1;
162                 break;    
163             } 
164         }
165         while((x<<1)<M){
166             int ls=(x<<1);
167             if(ck(min(miv,mi[ls]),you[ls])){
168                 miv=min(miv,mi[ls]);
169                 x=(x<<1|1);
170             }
171             else x=ls;
172         }
173         pos=x-H;
174 
175 
176         ans=max(calc(pos),ans);
177         //往左的第一个 mi[pos]<=rt
178         while(x!=1){
179             if(x&1){
180                 if(mi[x^1]<=rt){
181                     x=x^1;
182                     break;
183                 }
184             }
185             x>>=1;
186         }
187         pos=get_mi(x,1)-H;
188         ans=max(calc(pos),ans);
189 
190         x=ctr+H;miv=L+1;
191         while(x!=1){
192             x>>=1;
193             if(x&1){
194                 if(ck(min(miv,mi[x^1]),zuo[x^1])) miv=min(miv,mi[x^1]);
195                 else{
196                     x^=1;
197                     break;
198                 } 
199             }
200         }
201         while((x<<1)<M){
202             int rs=(x<<1|1);
203             if(ck(min(miv,mi[rs]),zuo[rs])){
204                 miv=min(miv,mi[rs]);
205                 x=(x<<1);
206             }
207             else x=rs;
208         }
209         pos=x-H;
210 
211         ans=max(calc(pos),ans);
212         //往右第一个
213         while(x!=1){
214             if((x&1)==0){
215                 if(mi[x|1]<=rt){
216                     x|=1;
217                     break;
218                 }
219             }
220             x>>=1;
221         }
222         pos=get_mi(x,0)-H;
223 
224         ans=max(calc(pos),ans);
225 
226         return ans;
227     }
228 }
229 using Tr::cg;
230 using Tr::Qr;
231 
232 struct Query{
233     int l,r,no;
234 }a[N];
235 bool cmp(Query aa,Query bb){
236     return aa.l>bb.l;
237 }
238 vector<int> ps[N];
239 int hd[N],no[N],bg[N];
240 
241 typedef long long ll;
242 ll ans[N];
243 
244 int main(){
245     scanf("%s",s+1);
246     L=n=strlen(s+1);
247 
248     m=rd();
249     rep(i,1,m){
250         s[++n]='#';
251         scanf("%s",s+1+n);
252         bg[i]=n;
253         n+=strlen(s+1+n);
254 
255         rep(j,bg[i]+1,n) hd[j]=i,no[j]=n-j+1;
256         a[i]=(Query){rd(),rd(),i};
257     }
258 
259     Sa::init();
260 
261     rep(i,1,n){
262         rep(j,sa[i],n) cerr<<s[j];
263         cerr<<endl;
264     }
265 
266     rep(i,1,n) ps[hd[sa[i]]].pb(i);
267 
268     sort(a+1,a+1+m,cmp);
269     Tr::build();
270     int R=L;
271     rep(i,1,m){
272         while(a[i].l<=R) cg(rk[R],R) ,R--;
273         rep(j,0,ps[a[i].no].size()-1){
274             int lcp=0,pos=ps[a[i].no][j];
275             if(j) lcp=Q(pos,ps[a[i].no][j-1]);
276             lcp=max(lcp,Qr(a[i].r,pos));
277 
278             if(no[sa[pos]]>lcp) ans[a[i].no]+=no[sa[pos]]-lcp;
279         }
280     }
281 
282     rep(i,1,m) printf("%lld
",ans[i]);
283     return 0;
284 }
285 //得到sa ht st存ht
286 //vector存每个询问的位置
287 
288 //l从大到小对询问排序
289 //处理T的重复子串
290 //线段树加点 线段树上二分最小值
原文地址:https://www.cnblogs.com/BLeaves/p/10547256.html