zoukankan      html  css  js  c++  java
  • [NOI2017]蚯蚓排队

    题意:有一些蚯蚓,每个蚯蚓都有一个小于等于6的值,有m次操作

      操作1:将两队蚯蚓连到一起

      操作2:在蚯蚓x的队列中将x及x之前和x之后分为两队

      操作3:给一个字符串s和整数k,对于s每个长度为k的字串,看有多少队蚯蚓中有这个子串,将每个子串的值乘起来作为答案

      观察数据,有一个条件是k<=50,所以直接采用哈希做,每次合并就暴力枚举新产生的长度<=50的串,最多250个,拆队同理,用哈希表去维护保证正确性,询问时维护长度为k的子串哈希值即可

      对于蚯蚓的前后关系,可以用链表维护

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    const int P=19260817;
    const int N=P+10;
    const int M=2e5+5;
    const int B=17;
    int read()
    {
      int ret=0,op=1,c=getchar();
      while(c>'9' || c<'0')
        {
          if(c=='-') op=-1;
          c=getchar();
        }
      while(c>='0' && c<='9')
        {
          ret=ret*10+c-'0';
          c=getchar();
        }
      return ret*op;
    }
    int ecnt=0,head[N];
    int n,m;
    int nxt[M],lst[M],val[M];//nxt和lst为维护蚯蚓位置的链表
    ll powB[100],pow10[100],qwq[100];
    char s[10000005];
    struct edge
    {
      int next,cnt,last;
      ll w;
    }e[N];
    void add(ll hash,ll q)//哈希表,如果有对于hash这个哈希值有q这个原值则cnt++,否则新开一个点
    {
      //printf("add:%lld:%lld
    ",hash,q);
      for(int i=head[hash];i;i=e[i].next)
        if(e[i].w==q)
          {
        e[i].cnt++;
        return ;
          }
      e[++ecnt].cnt=1;
      e[ecnt].w=q;
      e[ecnt].next=head[hash];
      head[hash]=ecnt;
    }
    void dlt(ll hash,ll q)//与add同理
    {
      for(int i=head[hash];i;i=e[i].next)
        if(e[i].w==q)
          {
        e[i].cnt--;
        return ;
          }
    }
    int query(ll hash,ll q)//与add同理
    {
      for(int i=head[hash];i;i=e[i].next) if(e[i].w==q) return e[i].cnt;
      return 0;
    }
    int s1[100],s2[100];
    void link(int u,int v)
    {
      int l1=0,l2=0;
      ll hsh=0,q=0,haxi=0,Q=0;
      nxt[u]=v;
      lst[v]=u;
      for(int i=u;i&&l1<49;i=lst[i])
        s1[++l1]=val[i],hsh=(hsh+val[i]*powB[l1-1])%P,q=q+val[i]*pow10[l1-1];//将u与u之前最长50的串取出来,注意在s1中的串是反着的
      for(int i=v;i&&l2<49;i=nxt[i]) s2[++l2]=val[i];
      for(int i=l1;i>=1;i--)//s1是反串,最前面的字符在l1的位置,所以倒序枚举
        {
          haxi=0;Q=0;
          for(int j=1;j<=l2&&i+j<=50;j++)
        {
          haxi=(haxi*B+s2[j])%P;
          Q=Q*10+s2[j];
          add((hsh*powB[j]%P+haxi)%P,q*pow10[j]+Q);
        }
          hsh=((hsh-powB[i-1]*s1[i])%P+P)%P;
          q=q-pow10[i-1]*s1[i];
        }
    }
    void cut(int u,int v)//与link同理
    {
      int l1=0,l2=0;
      ll hsh=0,q=0,haxi=0,Q=0;
      nxt[u]=0;
      lst[v]=0;
      for(int i=u;i&&l1<49;i=lst[i])
        s1[++l1]=val[i],hsh=(hsh+val[i]*powB[l1-1])%P,q=q+val[i]*pow10[l1-1];
      for(int i=v;i&&l2<49;i=nxt[i]) s2[++l2]=val[i];
      for(int i=l1;i>=1;i--)
        {
          haxi=0;Q=0;
          for(int j=1;j<=l2&&i+j<=50;j++)
        {
          haxi=(haxi*B+s2[j])%P;
          Q=Q*10+s2[j];
          dlt((hsh*powB[j]%P+haxi)%P,q*pow10[j]+Q);
        }
          hsh=((hsh-powB[i-1]*s1[i])%P+P)%P;
          q=q-pow10[i-1]*s1[i];
        }
    }
    int main()
    {
      n=read(),m=read();
      for(int i=1;i<=n;i++) val[i]=read(),qwq[val[i]]++;
      powB[0]=pow10[0]=1;
      for(int i=1;i<=50;i++) powB[i]=powB[i-1]*B%P,pow10[i]=pow10[i-1]*10;
      int op,x,y;
      while(m--)
        {
          op=read();
          if(op==1)
        {
          x=read(),y=read();
          link(x,y);
        }
          else if(op==2)
        {
          x=read();
          cut(x,nxt[x]);
        }
          else
        {
          int k;
          scanf("%s%d",s+1,&k);
          int len=strlen(s+1);
          ll hsh=0,q=0,ans=1;
          if(k==1)//特别的,如果询问长度为1的串,因为之前我也没有加过,所以在读入的时候开一个桶来统计答案
            {
              for(int i=1;i<=len;i++) ans=ans*qwq[s[i]-'0']%mod;
              printf("%lld
    ",ans%mod);
              continue;
            }
          for(int i=1;i<=k;i++)
            {
              hsh=(hsh*B+s[i]-'0')%P;
              q=q*10+s[i]-'0';
            }
          // printf("(%lld)%lld->",hsh,q);
                      ans=query(hsh,q)%mod;
          for(int i=k+1;i<=len;i++)
            {
              hsh=(((hsh-(s[i-k]-'0')*powB[k-1])%P+P)%P*B+s[i]-'0')%P;
              q=((q-(s[i-k]-'0')*pow10[k-1]))*10+s[i]-'0';
              //    printf("(%lld)%lld->",hsh,q);
                    ans=ans*query(hsh,q)%mod;
            }
          printf("%lld
    ",ans);
        }
        }
      return 0;
    }
  • 相关阅读:
    [CQOI2011]放棋子
    [JSOI2015]染色问题
    [ZJOI2016]小星星
    [NOI2018]你的名字
    bzoj2393 Cirno的完美算数教室
    [CQOI2012]局部极小值
    CF768F Barrels and boxes
    bzoj4402 Claris的剑
    烽火SATA SSD DSS200-B
    添加防火墙规则
  • 原文地址:https://www.cnblogs.com/pigba/p/9047533.html
Copyright © 2011-2022 走看看