• BZOJ3019 : [Balkan2012]handsome


    首先预处理出$f[i][j][k]$表示长度为$i$的序列,第一个位置是$j$,最后一个位置是$k$时合法的方案数。

    从后往前枚举LCP以及那个位置应该改成什么。

    用线段树维护区间内最左最右的已经确定的位置,以及区间内的合法方案数。

    合并的时候只需要将左右儿子的答案乘起来,然后再乘以左儿子最右到右儿子最左这一段区间的方案数即可。

    时间复杂度$O(nlog n)$。

    #include<cstdio>
    const int N=400010,M=1050000,P=1000000007;
    int n,m,i,j,k,x,a[N],g[3][3],f[N][3][3],pos[N],v[N],l[M],r[M],val[M],ans=1;char ch[9],s[N];
    inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
    inline void up(int&x,int y){x+=y;if(x>=P)x-=P;}
    void build(int x,int a,int b){
      l[x]=a,r[x]=b,val[x]=1;
      if(a==b){pos[a]=x;return;}
      int mid=(a+b)>>1;
      build(x<<1,a,mid),build(x<<1|1,mid+1,b);
    }
    inline bool change(int x,int p){
      if(~p){
        if(x>1&&~v[x-1])if(g[v[x-1]][p])return 0;
        if(x<n&&~v[x+1])if(g[p][v[x+1]])return 0;
      }
      v[x]=p,x=pos[x];
      if(p<0)l[x]=r[x]=0;
      for(x>>=1;x;x>>=1){
        l[x]=l[x<<1]?l[x<<1]:l[x<<1|1];
        r[x]=r[x<<1|1]?r[x<<1|1]:r[x<<1];
        val[x]=1LL*val[x<<1]*val[x<<1|1]%P;
        if(r[x<<1]&&l[x<<1|1])val[x]=1LL*val[x]*f[l[x<<1|1]-r[x<<1]+1][v[r[x<<1]]][v[l[x<<1|1]]]%P;
      }
      return 1;
    }
    inline int ask(){
      int ret=val[1],t,i;
      if(l[1]>1){
        for(t=i=0;i<3;i++)up(t,f[l[1]][i][v[l[1]]]);
        ret=1LL*ret*t%P;
      }
      if(r[1]<n){
        for(t=i=0;i<3;i++)up(t,f[n-r[1]+1][v[r[1]]][i]);
        ret=1LL*ret*t%P;
      }
      return ret;
    }
    int main(){
      read(n);
      for(i=1;i<=n;i++)read(a[i]);
      read(m);
      while(m--)scanf("%s",ch),g[ch[0]-'1'][ch[1]-'1']=1;
      scanf("%s",s+1);
      for(i=1;i<=n;i++)s[i]-='1',v[i]=s[i];
      for(i=0;i<3;i++)f[1][i][i]=1;
      for(i=1;i<n;i++)for(j=0;j<3;j++)for(k=0;k<3;k++)if(f[i][j][k])for(x=0;x<3;x++)if(!g[k][x])up(f[i+1][j][x],f[i][j][k]);
      build(1,1,n);
      for(i=n;i;change(a[i--],-1))for(j=0;j<s[a[i]];j++)if(change(a[i],j))up(ans,ask());
      return printf("%d",ans),0;
    }
    

      

  • 相关阅读:
    Java修改excel内容
    text标签onchang事件
    shh将数据导出excel
    正则表达式0到200以内的数
    虚拟内存与物理内存
    捕获内核的异常事件
    linux内存(三)内核与用户空间交互
    linux内存(二)高端内存
    linux内存(一) 内核空间与用户空间
    使用tc配置后端设备,来限制虚拟机网卡带宽
  • 原文地址:https://www.cnblogs.com/clrs97/p/6371367.html
Copyright © 2020-2023  润新知