3.23省选模拟

Angel,You are my faith

$T1$

结论题,但是我打了两个表都错了,人就没了

$n=4,\ 3 \ 2\ 4\ 1\ 5$

$n=5,\ 3 \ 4\ 2\ 5\ 1\ 6$

$n=6,\ 4 \ 3\ 5\ 2\ 6\ 1 \ 7$

好,考试时候我的打表程序前两个都跑错了...

看分别看奇数位置是$3\ 4 \ 5/ 3\ 2\ 1/4\ 5\ 6\ 7$

其实很显然了,倒着填,一个从小往大,一个从大往小

证明$:$

较为显然的,长度肯定是$n+1,$那么必然有一个重复的

如何构造出来一个表示不出来的

假设两个位置相等,无论怎么都不会是这种结构,那么就显然了,代码实现直接确定一个位置输出就好了

$T2$

也是个结论题(考场上的/平时的我太菜了看不出结论)

题意$:$

每次找出现两次的子串取出然后对取出的子串继续取出出现两次的子串,问最多能取多少次

结论$1:$除了第一个选串的情况,其余所有的情况,选后缀是不会变劣的

结论$2:$只要取出$endpoz$集合相同的位置取出来的次数一定是相同的,因为我们最后只是取出匹配位置

那么我们就可以用线段树维护是否存在,然后直接$dp$就好了,其实也不是$dp$,算是递推

#include<bits/stdc++.h>
#define MAXN 500005
using namespace std;
struct node
{
       int ls,rs;
}tr[MAXN<<4];
char s[MAXN];
int n,ls=1,tot=1,Num,Ans;
int trs[MAXN][26],pos[MAXN],len[MAXN],rt[MAXN],fa[MAXN],zx[MAXN],rk[MAXN],dp[MAXN],t[MAXN];
//好吧,我现在基本会出现位置的计算了 
void change(int &now,int l,int r,int poz)
{
     if(!now) now=++Num;
     if(l==r) return ;//我现在才明白...这个建出来就表示存在就好了!
     int mid=(l+r)>>1;
     if(poz<=mid) change(tr[now].ls,l,mid,poz);
     else change(tr[now].rs,mid+1,r,poz); 
}
int Merge(int rt1,int rt2,int l,int r)
{
     if(!rt1||!rt2) return rt1+rt2;
     int now=++Num;
     if(l!=r)
     {
         int mid=(l+r)>>1;
         tr[now].ls=Merge(tr[rt1].ls,tr[rt2].ls,l,mid);
         tr[now].rs=Merge(tr[rt1].rs,tr[rt2].rs,mid+1,r);
     }
     return now;
}
int query(int now,int l,int r,int L,int R)
{
    if(!now) return 0;
    if(l>=L&&r<=R) return 1;
    int mid=(l+r)>>1,res=0;
    if(L<=mid) res|=query(tr[now].ls,l,mid,L,R);
    if(R>mid)  res|=query(tr[now].rs,mid+1,r,L,R);
    return res;
}
void Insert(int c,int id)
{
     int p=ls;
     ls=++tot;
     int now=tot;
     len[now]=len[p]+1;
     pos[now]=id;
     for(;p&&!trs[p][c];p=fa[p]) trs[p][c]=now;
     if(!p) fa[now]=1;
     else
     {
          int q=trs[p][c];
          if(len[p]+1==len[q])
          {
              fa[now]=q;
          }
          else
          {
              int spilt=++tot;
              pos[spilt]=pos[q];
              for(int i=0;i<=25;i++)
              {
                  trs[spilt][i]=trs[q][i];
              }
              fa[spilt]=fa[q];
              len[spilt]=len[p]+1;
              fa[now]=fa[q]=spilt;
              for(;p&&trs[p][c]==q;p=fa[p]) trs[p][c]=spilt;
          }
     }
     //感觉这种线段树合并+SAM真的好常见
     change(rt[now],1,n,id);
//      cout<<"ls: "<<rt[now]<<" "<<id<<endl;
     //这里就是在每个串出现的位置线段树上加一下就好了,结尾位置 
}

void Sort()
{
     for(int i=1;i<=tot;i++) t[len[i]]++;
     for(int i=1;i<=n;i++) t[i]+=t[i-1];
     for(int i=1;i<=tot;i++) rk[t[len[i]]--]=i;
     //我们要线段树合并,就是fa和儿子之间
     //那么只需要按照长度排个序就好了,按照这个顺序合并就好了
     for(int i=tot;i>1;i--)
     {
         int u=rk[i],v=fa[u];
         rt[v]=Merge(rt[u],rt[v],1,n);
     }
}
int main()
{
    freopen("horse.in","r",stdin);
    freopen("horse.out","w",stdout);
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++)
    {
        Insert(s[i]-'a',i);
    }
    Sort();
    for(int i=2;i<=tot;i++)
    {
        int u=rk[i],v=fa[u];
        if(v==1) 
        {
           dp[u]=1;
           zx[u]=u;
        }
        //还有这个是小->大转移 
        else if(query(rt[zx[v]],1,n,pos[u]-len[u]+len[zx[v]],pos[u]-1))
        {
             dp[u]=dp[v]+1;
             zx[u]=u;
        }
        else
        {
            dp[u]=dp[v];
            zx[u]=zx[v];
        }
        Ans=max(Ans,dp[u]);
    }
    printf("%d",Ans);
    return 0;
}

 

$T3$

巨巨巨大大大数据结构题,$Give \ up$

 

posted @ 2022-03-23 16:51  Authentic_k  阅读(25)  评论(0编辑  收藏  举报