bzoj 做起走 -- bzoj 1009 GT 考试

  现在每次做一道bzoj上的题,整个人都感觉升华了。。。

  先是在网上各种搜题解。要么只有代码,要么有点讲解看不懂,对于从来没有耐心看完别人代码的我,只能一篇一篇的翻。。然后终于在某2011级同学的某段话中找到了灵感,把它给A了。

  我还是好好记录一下这道题的做题过程,不要又被其他人喷“只有做过的人才看得懂了!”

  首先说说这道题的思路吧:dp+矩阵优化。dp虽然不那么明显,但是做过了ac自动机上的dp之后也看得出来——kmp上的dp。具体怎么想到是dp的只能说是个人经验问题,做过一遍就容易做出来了。那么dp是什么呢?这种就跟ac自动机上的问题是一类的题,不过只有一个串来匹配,就kmp预处理咯。然后用一个二维的dp——f[i][j],i表示现在做到了第几位,j表示填完第i位后剩下了匹配到第几位。那么怎么转移呢?我们想,对于i-1转移到i位,其实和i是多少无关,i只和你转移是要加的那个值有关系,真真有关系的是i-1的已经匹配到了第j位。也就是说,每一次转移的,其实是一样的,也就是说我们可以预处理出i-1的j转移到i,然后存下来。

  所以转移方程: f[i][j] = f[i-1][0]*pre[0][j] + f[i-1][1]*pre[1][j] +
f[i-j][2]*pre[2][j] + ......
f[i-1][m-1]*pre[m-1][j];(没有f[i-1][m]是因为这种情况是不合法的)

  那么怎么求出这样的处理呢?
使用kmp的next数组,从i位置开始,然后开始枚举第i+1位的数字开始向后匹配,然后找到可以匹配的最大的那个位置,基本写法参照kmp的匹配。每找到某一个位置,把那个位置的+1即可。

  但n是10^9,然后一看这个递推式子,就是矩阵乘法的形式啊!所以直接矩阵快速幂吧!

  顺便一说,归纳一下矩阵快速幂的基本的题型:转移方程中的系数在多次转移中不改变,且递推式是一阶的,然后一看数据特别大的,就可以考虑了。

  code:

  

+ ?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using
namespace std;

const
int maxn = 22;

int
n,m,mod;

int
str[maxn];

int
next[maxn];

struct
mtr{

    int
mx[maxn][maxn];

    mtr(){memset(mx,0,sizeof(mx));}

}modu;

void
self_build(){

    next[1] = 0;

    int
j = next[1];

    for(int
i = 2; i <= m; i++){

        while(j && str[j+1] != str[i]) j = next[j];

        if(str[j+1] == str[i]) j++;

        next[i] = j;

    }

}

mtr cf(mtr a, mtr b,int
xm,int
xn,int
xk){

    mtr c;

    for(int
i = 0; i < xm; i++)

        for(int
j = 0; j < xk; j++)

            for(int
k = 0;k < xn; k++){

                c.mx[i][j] = (c.mx[i][j]%mod+((a.mx[i][k]%mod) * (b.mx[k][j])%mod)%mod)%mod;

            }

    return
c;

}

mtr qpow(mtr a,int
len,int
b){

    mtr tmp;

    mtr re;

    for(int
i = 0;i < len; i++)

        for(int
j = 0; j < len; j++)

            tmp.mx[i][j] = a.mx[i][j];

    for(int
i = 0; i < len; i++)

        re.mx[i][i] = 1;

    while(b){

        if(b&1) re = cf(re,tmp,len,len,len);

        tmp = cf(tmp,tmp,len,len,len);

        b >>= 1;

    }

    return
re;

}

int
fans;

mtr ans;

int
main(){

    //freopen("cs.in","r",stdin);

    scanf("%d%d%d",&n,&m,&mod);

    getchar();

    for(int
i = 1; i <= m; i++){

        str[i] = getchar() - ‘0‘;

    }

    self_build();

    for(int
i = 0; i < m; i++){

        for(int
j = 0;j <= 9; j++){

            int
tmp = i;

            while(tmp && str[tmp+1] != j) tmp = next[tmp];

            if(str[tmp+1] == j) modu.mx[i][tmp+1] = (modu.mx[i][tmp+1]+1)%mod;

            else
modu.mx[i][0] = (modu.mx[i][0]+1)%mod;

        }

    }

    ans.mx[0][0] = 1;

    modu = qpow(modu,m,n);

    ans = cf(ans,modu,1,m,m);

    fans = 0;

    for(int
i = 0; i < m; i++)

        fans = (fans%mod + ans.mx[0][i]%mod)%mod;

    printf("%d",fans);

    return
0;

}

顺便再说一句,矩阵可以套在struct里面,好写些,具体可参见代码。

时间: 2024-11-12 10:28:05

bzoj 做起走 -- bzoj 1009 GT 考试的相关文章

【BZOJ做题记录】07.07~?

在NOI一周前重开一个坑 最后更新时间:7.07 11:26 7.06 下午做的几道CQOI题: BZOJ1257: [CQOI2007]余数之和sum:把k mod i写成k-k/i*i然后分段求后面的部分就好了 BZOJ1258: [CQOI2007]三角形tri:在草稿纸上按照位置和边找一下规律就好了 BZOJ1260: [CQOI2007]涂色paint:简单的区间DP BZOJ1303: [CQOI2009]中位数图:小于中位数的改为-1大于的改为1,算一算前缀和然后哈希一下乘一乘就好

BZOJ 3143 游走 | 数学期望 高斯消元

啊 我永远喜欢期望题 BZOJ 3143 游走 题意 有一个n个点m条边的无向联通图,每条边按1~m编号,从1号点出发,每次随机选择与当前点相连的一条边,走到这条边的另一个端点,一旦走到n号节点就停下.每经过一条边,要付出这条边的编号这么多的代价.现将所有边用1~m重新编号,使总代价的期望最小,求这个最小值. 题解 我们可以求出每条边的期望经过次数,然后贪心地让经过次数多的边编号小即可. 直接用边来列方程求经过次数似乎列不出来,我们借助点来列方程. 设x[u]为从某个点出发的次数的期望,v为与u

BZOJ 1009 GT考试

首先,f[i][j]表示准考证后i个和不吉利数字前j个匹配种类数. 于是f[i][j]=Σf[i-1][k]*g[k][j],其中g为匹配k个到匹配j个的方案数.(暴力预处理) 然后矩阵快速幂即可,注意不能从匹配m个状态转出来. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,k,p[25],top=0

bzoj 2707 [SDOI2012]走迷宫(SCC+高斯消元)

Description Morenan被困在了一个迷宫里.迷宫可以视为N个点M条边的有向图,其中Morenan处于起点S,迷宫的终点设为T.可惜的是,Morenan非常的脑小,他只会从一个点出发随机沿着一条从该点出发的有向边,到达另一个点.这样,Morenan走的步数可能很长,也可能是无限,更可能到不了终点.若到不了终点,则步数视为无穷大.但你必须想方设法求出Morenan所走步数的期望值. Input 第1行4个整数,N,M,S,T 第[2, M+1]行每行两个整数o1, o2,表示有一条从o

BZOJ做题记录[0512~?]

觉得做一道开一篇真不好...好多想找的东西都被刷下去了... 至于?的日期究竟到什么时候...还是看心情...但是估计不会超过七天吧 05.12 当然是用刷水题来开启新的一天>< 感觉啊...这道题怎么做都可以..所以索性不说了 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<cmath> 5 #define INF 1000000007 6 #defin

BZOJ 3143 游走(高斯消元)

题意:一个无向连通图,顶点从1编号到n,边从1编号到m.小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数.当小Z 到达n号顶点时游走结束,总分为所有获得的分数之和. 现在,请你对这m条边进行编号,使得小Z获得的总分的期望值最小. 思路:显然,需要求出每条边的期望经过次数,然后排序贪心赋值即可,但是每条边的期望经过次数是什么呢? 是 E(e)=E(u)/D(u) + E(v)/D(v) (u,v∈e

BZOJ 3143 游走

ans=Σf[e]*w[e],其中f[e]表示e期望经过的次数. 卡精度差评.差评.差评. 但是高消写法得改改了.... #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxv 1050 #define maxe 1000050 #define eps 1e-10 using namespace s

[BZOJ 3236] [Ahoi2013] 作业 &amp;&amp; [BZOJ 3809] 【莫队 | 分块】

题目链接: BZOJ - 3236   BZOJ - 3809 算法一:莫队 首先,单纯的莫队算法是很好想的,就是用普通的第一关键字为 l 所在块,第二关键字为 r 的莫队. 这样每次端点移动添加或删除一个数字,用树状数组维护所求的信息就是很容易的.由于这里有 logn复杂度,所以复杂度还是挺高的. 于是 BZOJ-3236 的时限 100s,我的代码跑了 98s,险过...... However..BZOJ-3809 的出题人(SLYZ的神犇)就没有这么善良了!直接内存限制 28MB 就直接把

Bzoj 2453: 维护队列 &amp;&amp; Bzoj 2120: 数颜色 分块,bitset

2453: 维护队列 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 578  Solved: 247[Submit][Status][Discuss] Description 你小时候玩过弹珠吗? 小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N.为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少.当然,A有时候会依据个人喜好,替换队列中某个弹珠的颜色.但是A还没有学过编程,且觉得头脑风暴太浪费脑力了,所以向你