• BZOJ3537 : [Usaco2014 Open]Code Breaking


    考虑容斥,枚举哪些串必然出现,那么贡献为$(-1)^{选中的串数}$。

    设$f[i][j]$表示$i$的子树内,$i$点往上是$j$这个串的贡献之和,那么总状态数为$O(n+m)$,用map存储$f$。

    将子树的DP值与父亲合并时,按串长分类讨论:

    若子树串比较长,那么暴力枚举它的前缀状态转移即可。

    若父亲串比较长,那么一个子树状态对父亲状态的贡献编码后对应一个区间。

    扫描线处理区间,对于相邻的区间内的所有DP值整体乘上一个数,线段树维护。

    最后沿着线段树上有效节点走,即可不重不漏地得到所有新的状态。

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

    #include<cstdio>
    #include<map>
    #include<algorithm>
    using namespace std;
    typedef map<int,int>T;
    const int N=20010,M=860000,MAXN=2100000,U=300010,P=1234567;
    int n,m,i,j,x,y,g[N],nxt[N],sub[M],right[M],ans;char ch[9];
    T f[N];T::iterator k;int tmp[U],tag[MAXN];bool s[MAXN];
    struct E{int x,y;E(){}E(int _x,int _y){x=_x,y=_y;}}q[U],e[U*2];
    inline bool cmp(const E&a,const E&b){return a.x<b.x;}
    inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;}
    inline void tag1(int x,int p){tag[x]=1LL*tag[x]*p%P;}
    inline void pb(int x){if(tag[x]!=1)tag1(x<<1,tag[x]),tag1(x<<1|1,tag[x]),tag[x]=1;}
    void build(int x,int a,int b){
      if(a==b)return;
      tag[x]=1;
      int mid=(a+b)>>1;
      build(x<<1,a,mid),build(x<<1|1,mid+1,b);
    }
    void ins(int x,int a,int b,int c,int p){
      if(a==b){
        up(tag[x],p);
        s[x]=!!tag[x];
        return;
      }
      pb(x);
      int mid=(a+b)>>1;
      if(c<=mid)ins(x<<1,a,mid,c,p);else ins(x<<1|1,mid+1,b,c,p);
      s[x]=s[x<<1]|s[x<<1|1];
    }
    void change(int x,int a,int b,int c,int d,int p){
      if(c<=a&&b<=d){tag1(x,p);return;}
      pb(x);
      int mid=(a+b)>>1;
      if(c<=mid)change(x<<1,a,mid,c,d,p);
      if(d>mid)change(x<<1|1,mid+1,b,c,d,p);
    }
    int ask(int x,int a,int b,int c){
      if(a==b)return tag[x];
      pb(x);
      int mid=(a+b)>>1;
      return c<=mid?ask(x<<1,a,mid,c):ask(x<<1|1,mid+1,b,c);
    }
    void dfs(int x,int a,int b,T&f){
      if(!s[x])return;
      if(a==b){
        if(tag[x])up(f[a<<4&1048575],tag[x]);
        tag[x]=s[x]=0;
        return;
      }
      pb(x);
      int mid=(a+b)>>1;
      dfs(x<<1,a,mid,f),dfs(x<<1|1,mid+1,b,f);
      s[x]=0;
    }
    inline void solve(){
      int i,j,cnt=0,s=0;
      for(i=0;i<m;i++){
        tmp[i]=0;
        for(j=sub[q[i].x];~j;j=sub[j])up(tmp[i],ask(1,0,M,j));
        e[++cnt]=E(q[i].x,q[i].y);
        e[++cnt]=E(right[q[i].x],P-q[i].y);
      }
      sort(e+1,e+cnt+1,cmp);
      for(i=1;i<=cnt;i++){
        if(i>1&&e[i].x>e[i-1].x)change(1,0,M,e[i-1].x,e[i].x-1,s);
        up(s,e[i].y);
      }
      for(i=0;i<m;i++)if(tmp[i])ins(1,0,M,q[i].x,1LL*q[i].y*tmp[i]%P);
    }
    int main(){
      scanf("%d%d",&n,&m);
      for(i=2;i<=n;i++)scanf("%d",&x),nxt[i]=g[++x],g[x]=i;
      while(m--){
        scanf("%d%s",&x,ch);
        for(y=j=0;j<5;j++)y=y<<4|(ch[j]-'0'+1);
        f[x+1][y]=P-1;
      }
      for(sub[0]=-1,right[0]=M,i=1;i<M;i++)for(j=0;;j++)if(i>>(j*4)&15){
        sub[i]=i-(((i>>(j*4))&15)<<(j*4));
        right[i]=i+(1<<(j*4));
        break;
      }
      build(1,0,M);
      for(i=n;i;i--){
        for(j=1;j<=10;j++)f[i][j<<16]=1;
        for(k=f[i].begin();k!=f[i].end();k++)ins(1,0,M,k->first,k->second);
        for(j=g[i];j;j=nxt[j]){
          m=0;
          for(k=f[j].begin();k!=f[j].end();k++)q[m++]=E(k->first,k->second);
          solve();
        }
        f[i].clear();
        dfs(1,0,M,f[i]);
      }
      for(ans=i=1;i<=n;i++)ans=ans*10%P;
      up(ans,P-f[1][0]);
      return printf("%d",ans),0;
    }
    

      

  • 相关阅读:
    WORD窗体保护密码清除
    在Fedora 23 Server和Workstation上安装LAMP(Linux, Apache, MariaDB和PHP)
    firefox 提示 ssl_error_unsupported_version 的解决方法
    算法题:求一个序列S中所有包含T的子序列(distinct sub sequence)
    Django模板输出Dict所有Value的效率问题
    笔记:LNK2001不代表链接器真的需要链接相关符号
    Windows Restart Manager 重启管理器
    advapi32.dll kernel32.dll 中的两套注册表API
    使用GDI+保存带Alpha通道的图像(续)
    使用GDI+保存带Alpha通道的图像
  • 原文地址:https://www.cnblogs.com/clrs97/p/7468630.html
Copyright © 2020-2023  润新知