CQOI2016 bzoj4523 路由表

题意


传送门
这个题我真的是想请语文家教了QAQ题意极其难懂……而且重庆场上给的样例并没有bzoj这么大……各种理解样例都说得通……而且那个Hint也是后来问了之后才有的……
大概就是求从1开始匹配的过程中,在 [L,R] 以内变化的次数。定义匹配指A串作为Q串的前缀。定义变化指已知串作为给定串的匹配变长。





















题解


因为涉及到匹配,可以容易地想到Trie。因为涉及到区间的查询,可以容易地想到可持久化Trie。
关键在于怎么处理最长匹配前缀的变更这个问题。
有的做法是单调栈。
我的做法有点类似,但是实现做了简化。考虑当前插入的一个串,其可以更新的串就是那些需要匹配的询问串从前往后匹配到它的时候,匹配长度都没有它长的串。想到我们询问一个串的时候需要找的是最多能匹配多长。于是我们在插入的时候记录一个值,使其为更新路径上最下面的Sum值,令插入结束节点的Sum为该值+1。同理,查询的时候也要找最下面的Sum值,对于Rt[R]和Rt[L-1]查询到的值做一个减法就是答案了。


代码

#include<cstdio>
#include<cstring>
using namespace std;

const int NUM=1000005;
const int TOT=NUM*35;

int n,m,Rt[NUM],tot,t[5];
int ch[TOT][2],Sum[TOT],tmp[35];

void Trans(int len)
{   int p=0,i,j;
    for(i=0;i<4;i++)
     for(j=7;j>=0;j--)
     { p++;tmp[p]=(t[i]>>j)&1;}
    /* for(int i=1;i<=p;i++) { printf("%d",tmp[i]); if(i==len)putchar('|'); if(i%8==0)putchar(' '); }putchar('\n'); */
}

int Len[1005],s[1005][35];
void A1(int Tim)
{   int i,len;
    scanf("%d.%d.%d.%d/%d",&t[0],&t[1],&t[2],&t[3],&len);
    Len[Tim]=len;Trans(len);
    for(i=1;i<=32;i++)s[Tim][i]=tmp[i];
}

int C1(int Up,int len)
{   int k,i,ret=0,Max=0;
    for(k=1;k<=Up;k++)
    { if(Len[k]<Max)continue;///!!!!!
      for(i=1;i<=Len[k];i++)
       if(s[k][i]!=tmp[i])break;
      if(i<=Len[k])continue;
      Max=Len[k];ret++;
    }
    return ret;
}
void Q1()
{   int i,len=32,L,R;
    scanf("%d.%d.%d.%d %d %d",&t[0],&t[1],&t[2],&t[3],&L,&R);
    Trans(len);
    printf("%d\n",C1(R,len)-C1(L-1,len));
}
void Solve1()
{   char OP[10];
    n=0;
    while(m--)
    { scanf("%s",OP);
      if(OP[0]=='A'){n++;A1(n);}
      if(OP[0]=='Q')Q1();
    }
}

void Add(int Tim)
{   int now,lst,i,w,len,ret=0;
    scanf("%d.%d.%d.%d/%d",&t[0],&t[1],&t[2],&t[3],&len);
    Trans(len);
    now=Rt[Tim]=++tot;lst=Rt[Tim-1];
    memcpy(ch[now],ch[lst],sizeof(ch[lst]));
    for(i=1;i<=len;i++)
    { w=tmp[i];ch[now][w]=++tot;
      now=ch[now][w];lst=ch[lst][w];
      memcpy(ch[now],ch[lst],sizeof(ch[lst]));
      Sum[now]=Sum[lst];
      if(Sum[lst])ret=Sum[lst];
    }
    Sum[now]=ret+1;///!!!!!!!!!!!!!!!!
}

int Count(int now,int len)
{   int i,w,ret=0;now=Rt[now];
    for(i=1;i<=len;i++)
    { w=tmp[i];
      now=ch[now][w];
      if(!now)break;
      if(Sum[now])ret=Sum[now];
    }
    return ret;
}
void Query()
{   int i,len=32,L,R;
    scanf("%d.%d.%d.%d %d %d",&t[0],&t[1],&t[2],&t[3],&L,&R);
    Trans(len);
    printf("%d\n",Count(R,len)-Count(L-1,len));
}

void Solve2()
{   char OP[10];
    n=0;Rt[0]=0;
    memset(ch[0],0,sizeof(ch[0]));Sum[0]=0;tot=0;
    while(m--)
    { scanf("%s",OP);
      if(OP[0]=='A'){n++;Add(n);}
      if(OP[0]=='Q')Query();
    }
}
int main(){
    freopen("route.in","r",stdin);
    freopen("route.out","w",stdout);

    //t[0]=255;t[1]=255;t[2]=255;t[3]=255;
    //Trans(32);
    scanf("%d",&m);
    if(m<=1000)Solve1();
    else Solve2();

    fclose(stdin);fclose(stdout);
    return 0;
}


那两个 //!!!!! 是因为不知道匹配规则的时候加的防止忘掉的标记……我的代码调试有点多哈……

你可能感兴趣的:(OI-题解)