• 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 //线段树加点 线段树上二分最小值
  • 相关阅读:
    集合框架
    hashtable
    测试3
    opcache的威力
    信息的信息
    php blog to explore
    BEHAT安装
    Failed to start: SocketListener0@0.0.0.0:4444
    模板方法设计模式
    mysqldump
  • 原文地址:https://www.cnblogs.com/BLeaves/p/10547256.html
Copyright © 2020-2023  润新知