• [atARC110E]Shorten ABC


    考虑令$a$、$b$和$c$分别对应1、2和3,那么每一次相当于令$x$和$y$变为$xoplus y$(要求$x e y$)

    根据异或的结合律,我们相当于将其划分为若干个区间求异或值

    (另外还有$x e y$的条件,归纳可证等价于要求区间异或值不为0且区间内字母不完全相等或仅有1个字母)

    为了保证每一种方案都不同,强制除了第一个以外的区间任意非空前缀异或值都不为0,否则可以把该前缀加入到上一个区间中(同时此时要保证了$x e y$的条件)

    令$f_{i}$表示前$i$个字母对应的方案数,考虑递推转移,设$j$为第一个异或前缀和等于$i$的,那么就转移到$(i,j)$,区间修改可以用差分来维护

    初始状态需要考虑一下,首先需要保证异或不为0,然后若所有字母完全相同(必然是奇数个),可以理解为最后$len-1$个字母是由下一段一个非空前缀补上来的,因此允许出现

    特别的,当所有字母都相同时答案为1,需要特判

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 1000005
     4 #define mod 1000000007
     5 int n,a[N],nex[N],lst[4],f[N];
     6 char s[N];
     7 void update(int l,int r,int x){
     8     f[l]=(f[l]+x)%mod;
     9     if (r+1<n)f[r+1]=(f[r+1]+mod-x)%mod;
    10 }
    11 int main(){
    12     scanf("%d%s",&n,s);
    13     for(int i=0;i<n;i++)a[i]=s[i]-'A'+1;
    14     bool flag=0;
    15     for(int i=1;i<n;i++)
    16         if (s[i]!=s[0])flag=1;
    17     if (!flag){
    18         printf("1");
    19         return 0;
    20     } 
    21     for(int i=1;i<n;i++)a[i]^=a[i-1];
    22     for(int i=0;i<4;i++)lst[i]=n;
    23     for(int i=n-1;i>=0;i--){
    24         nex[i]=lst[a[i]];
    25         lst[a[i]]=i;
    26     }
    27     for(int i=0;i<n;i++)
    28         if (a[i])update(i,i,1);
    29     for(int i=0;i<n;i++){
    30         if (i)f[i]=(f[i]+f[i-1])%mod;
    31         update(i+1,nex[i]-1,f[i]);
    32     }
    33     printf("%d",f[n-1]);
    34 } 
    View Code
  • 相关阅读:
    【费用流】【CODEVS】1227 方格取数2
    【CODEVS】1034 家园
    【BZOJ】1066: [SCOI2007]蜥蜴
    【最大流】【CODEVS】1993 草地排水
    【HDU】2191 多重背包问题
    【TYVJ】1520 树的直径
    【BZOJ】1984 月下“毛景树”
    【BZOJ】1588: [HNOI2002]营业额统计
    【NOI】2004 郁闷的出纳员
    【POJ】2892 Tunnel Warfare
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14112042.html
Copyright © 2020-2023  润新知