zoukankan      html  css  js  c++  java
  • 【BZOJ1009】【HNOI2008】GT考试

    依旧看人代码写,不过我觉得自己慢慢写一个也可以写成?

    原题:

    阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
    他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为
    0

    N<=10^9,M<=20,K<=1000

    一开始完全是懵逼的,后来找到两个解释结合一下就理解了

    用a(i,j)表示原串走到i,不吉利的串走到j的方案数

    设一个数组b(i,j),表示从(i,j)转移到(i+1,k)的方案数,这个可以用kmp处理,先kmp出next,然后枚举i和j,根据kmp求出k并将b(i+1,k)++

    然后这个b表示的就是a中的元素下一步会贡献到哪里

    这个东西比较玄,只能勉强意会……

    NOIP吧里有一种解释,虽然写法似乎和我的不太一样,引导思路效果不错:

    “构造转移矩阵A和列向量B,B=(f[0][0],f[0][1],...,f[0][m]),A可以由DP得到,那么A*B的结果就是(f[1][0],f[1][1],...,f[1][m]),所以A^n*B的结果就是(f[n][0],f[n][1],...,f[n][m])”

    然后矩阵快速乘即可

    小技巧:使用a&1判断奇偶

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 int n,m,mo,s[30];
     8 int next[30];
     9 int b[30][30],a[30][30],c[30][30];
    10 int ans=0;
    11 void kmp(){
    12     int temp=0;  next[1]=0;
    13     for(int i=2;i<=m;i++){
    14         while(temp && s[temp+1]!=s[i])  temp=next[temp];
    15         if(s[temp+1]==s[i])  temp++;
    16         next[i]=temp;
    17     }
    18 }
    19 void get_b(){
    20     int temp;
    21     for(int i=0;i<m;i++){//注意j枚举的是下一位,因为这里j是对下一位的转移,所以矩阵乘法从0开始写,比较方便
    22         a[i][i]=1;
    23         for(int j=0;j<=9;j++){
    24             temp=i;
    25             while(temp && s[temp+1]!=j)  temp=next[temp];
    26             if(j==s[temp+1])  b[i][temp+1]+=1;
    27             else  b[i][0]+=1;
    28         }
    29     }
    30 }
    31 void fast_mi(){
    32     while(n){
    33         if(n&1){//快速判断奇偶
    34             for(int i=0;i<m;i++)
    35                 for(int j=0;j<m;j++){
    36                     c[i][j]=0;//反正也要枚举,就不用memset了
    37                     for(int k=0;k<m;k++)
    38                         c[i][j]=(c[i][j]+b[i][k]*a[k][j])%mo;
    39                 }
    40             for(int i=0;i<m;i++)
    41                 for(int j=0;j<m;j++)
    42                     a[i][j]=c[i][j];
    43         }
    44         n>>=1;
    45         for(int i=0;i<m;i++)
    46             for(int j=0;j<m;j++){
    47                 c[i][j]=0;
    48                 for(int k=0;k<m;k++)
    49                     c[i][j]=(c[i][j]+b[i][k]*b[k][j])%mo;
    50             }
    51         for(int i=0;i<m;i++)
    52             for(int j=0;j<m;j++)
    53                 b[i][j]=c[i][j];
    54         /*for(int i=0;i<m;i++){
    55             for(int j=0;j<m;j++)
    56                 cout<<a[i][j]<<" ";
    57             cout<<endl;
    58         }*/
    59     }
    60 }
    61 int main(){//freopen("ddd.in","r",stdin);
    62     memset(b,0,sizeof(b));
    63     cin>>n>>m>>mo;
    64     for(int i=1;i<=m;i++){
    65           scanf("%c",&s[i]);  while(s[i]<'0'||s[i]>'9')  scanf("%c",&s[i]);
    66         s[i]-='0';
    67     }
    68     kmp();  get_b();
    69     fast_mi();
    70     for(int i=0;i<m;i++)  ans=(ans+a[0][i])%mo;
    71     cout<<ans<<endl;
    72     return 0;
    73 }
    View Code
  • 相关阅读:
    C# 打印PPT幻灯片
    Java 创建/识别条形码、二维码
    Java 添加Word文本框
    Java 复制PPT幻灯片
    C# 读取Word内容控件
    Java 操作Word书签(三):用文本、图片、表格替换书签
    Java 操作Word书签(二):添加文本、图片、表格到书签内容
    C#/Java 动态生成电子发票
    C# 复制Excel单元格格式
    Java 操作Word书签(一):添加、删除、读取书签
  • 原文地址:https://www.cnblogs.com/JSL2018/p/5919618.html
Copyright © 2011-2022 走看看