zoukankan      html  css  js  c++  java
  • BZOJ 2745: [HEOI2012]Bridge

    2745: [HEOI2012]Bridge

    Time Limit: 30 Sec  Memory Limit: 128 MB
    Submit: 199  Solved: 90
    [Submit][Status][Discuss]

    Description

    fyg背着他的电脑来到河北省来,就是为了见一眼古老的赵州桥。 
    终于,他来到了赵州桥,放下了电脑,正准备休息。一阵风吹来,从中闪现出一人影。fyg只觉天昏地暗,待得再次睁开眼时,发觉自己已经到了一神奇的国度,置身于一巨大的圆盘之上。放眼看去,四周都是奇形怪状的桥,不远处有一老头盘膝而坐。 fyg还沉浸在惊奇之中,老头(难道就是传说中走过赵州桥的张老头!!)便开口了:凡人,你现在在我的世界中,想要出去就要回答我的问题。fyg只得点头,老头继续道:你现在要去闯关,我给你m种颜色,总共有n关(神仙也懂数学,表示压力巨大。。==)。每一关中有一座桥,在第i关中,桥长度有i个单位,每个单位长度上有2个格子(也就是说这座桥有2i个格子),现在你要计算出:在这座桥上涂色使得桥上相邻格子的颜色不一样总方案数,然后再乘上(2*i)^m。如在第1关,若你手上有2种颜色,分别为蓝色和绿色。则总方案数为2*2*2 =8种,涂色方案数为2(如下图,旋转、翻转相同算不同的方案),然后还要再乘2个2,最后你出来之后我会问你所有关中计算出来的数的和。如果你能答对,我就可以让你出去了,否则就无限轮回吧。 
    fyg表示这个问题太水了,完全不想算。。。于是, 他马上打开电脑上了QQ找到了喜欢计算的你,求你 帮他直接把最终 答案算出来,让他回到赵州桥上。这两个数都有可能很大,fyg 不想为难你,所以你只要告诉他其除以p的余数。

    Input

    只有一行,其中包含四个正数n、m、p,分别由一个空格分开。n、m、p含义和题目描 述一致。 

    Output

    一行,表示方案数的和除以p的余数。 

    Sample Input

    2 5 50

    Sample Output

    30
    【样例说明】
    总共有2关。
    第一关的桥长度为1,总共有2个格子,涂色方案数为20,再乘上2 ^ 5,第一关中 计算出的数为640。
    第二关的桥长度为2,总共有4个格子,涂色方案数为260,再乘上4 ^ 5,第二关中 计算出的数为266240。
    两个数字加起来除以50余30,故输出为30。

    HINT

    【数据范围】 

    对于其中25%的数据,满足 n <= 10^6,m <= 200,p <= 10^9; 对于其中40%的数据,满足 n <= 10^9,m <= 120,p <= 10^9; 对于其中15%的数据,满足 n <= 10^9,m <= 200,p <= 10^9; 对于最后20%的数据,满足 n<= 10^9,m <= 3000,p <= 3000;

    Source

     
    [Submit][Status][Discuss]

    写了一天的二逼题,KCUF

    首先说一下,题目中的桥是2xN的,而不是1x2N的,别想错了,不然就真的走远了。

    然后可以手推一下样例,发现是个简单的DP,甚至连DP都称不上,就是个统计问题,这时你应该得到了一个式子——

    $answer=2^{m}(m^{2}-m)sum_{i=1}^{n}{i^{m}(m^{2}-3m+3)^{i-1}}$,推不出来还是洗洗睡吧。

    然后看到数据范围,发现有25points是给暴力的,$O(NlogM)$就可以拿到。

    然后看出下面的数据要分两种做法——一种针对m较大但是p较小的,一种针对m较小但是p很大的。

    m较大,p较小

    发现$i^{m}$这一项,在$mod p$意义下有很有意思的性质——$i^{m}=(i mod p)^{m}$。

    哎,那岂不是至多每p项$i^{m}$就会出现一个循环吗?而每个循环节之间又是$(m^{2}-3m+3)^{p}$的等比关系(在此默认循环节长度为p),那就暴力求出第一段和公比,就是等比数列求和。蛋疼的是p不一定是素数,所以想用等比公式是不行的,因为没有逆元。然后就可以倍增法或矩阵快速幂。(小生一开始写的倍增,结果越写越乱,最后还是用矩阵幂省心)

    m较小,p较大

    还是想法搞掉$i^{m}$这一项。发现$i^{m}=[(i-1)+1]^{m}$,然后可以二项式一下就可以搞成DP形式了,$f[i][j]=i^{j}(m^{2}-3m+3)^{i-1}$,f[i]可以从f[i-1]推出来,构造转移矩阵,跑矩阵快速幂并维护前缀和即可。

      1 #include <cstdio>
      2 #include <cstring>
      3 
      4 typedef long long lnt;
      5 
      6 int n, m, p;
      7 
      8 inline int pow(lnt a, int b)
      9 {
     10     lnt r = 1;
     11     
     12     while (b)
     13     {
     14         if (b & 1)
     15             r = r * a % p;
     16         
     17         b = b >> 1;
     18         a = a * a % p;
     19     }
     20     
     21     return r;
     22 }
     23 
     24 namespace case1
     25 {    // m <= 200
     26     int lim;
     27     int ans;
     28 
     29     int C[205][205];
     30     
     31     inline void calculateC(void)
     32     {
     33         for (int i = 0; i <= lim; ++i)
     34         {
     35             C[i][0] = 1;
     36             
     37             for (int j = 1, k = 2; j <= i; j += 2, k += 2)
     38             {
     39                 C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
     40                 C[i][k] = C[i - 1][k - 1] + C[i - 1][k];
     41                 
     42                 if (C[i][j] >= p)C[i][j] -= p;
     43                 if (C[i][k] >= p)C[i][k] -= p;
     44             }
     45         }
     46     }
     47 
     48     int M[205][205];
     49     
     50     inline void calculateM(void)
     51     {
     52         int bas = (m * m - 3*m + 3) % p;
     53             
     54         for (int i = 0; i <= m; ++i)
     55             for (int j = i; j <= m; ++j)
     56                 M[i][j] = 1LL * bas * C[j][i] % p;
     57                 
     58         M[m][lim] = M[lim][lim] = 1;
     59     }
     60     
     61     int R[205];
     62     
     63     inline void calculateR(void)
     64     {
     65         for (int i = 0; i <= m; ++i)R[i] = 1;
     66         
     67         for (int t = n; t; t >>= 1)
     68         {
     69             if (t & 1)
     70             {
     71                 static int T[205];
     72                 
     73                 memset(T, 0, sizeof T);
     74                 
     75                 for (int i = 0; i <= lim; ++i)
     76                     for (int j = 0; j <= lim; ++j)
     77                         T[j] = (T[j] + 1LL * R[i] * M[i][j]) % p;
     78                         
     79                 memcpy(R, T, sizeof R);
     80             }
     81             
     82             {
     83                 static int T[205][205];
     84                 
     85                 memset(T, 0, sizeof T);
     86                 
     87                 for (int i = 0; i <= lim; ++i)
     88                     for (int k = 0; k <= lim; ++k)if (M[i][k])
     89                         for (int j = 0; j <= lim; ++j)if (M[k][j])
     90                             T[i][j] = (T[i][j] + 1LL * M[i][k] * M[k][j]) % p;
     91                 
     92                 memcpy(M, T, sizeof M);
     93             }
     94         }
     95     }
     96     
     97     inline void main(void)
     98     {
     99         lim = m + 1;
    100         
    101         calculateC();
    102         calculateM();
    103         calculateR();
    104         
    105         ans = R[lim];
    106         
    107         ans = (1LL * ans * pow(2, m)) % p;
    108         ans = (1LL * ans * (m*m - m)) % p;
    109         
    110         printf("%d
    ", (ans + p) % p);
    111     }
    112 }
    113 
    114 namespace case2
    115 {    // p <= 3000
    116     int bas;
    117     int cnt;
    118     int ans;
    119     
    120     int C[2];
    121     
    122     inline void calculateC(void)
    123     {
    124         bas = (m * m - 3*m + 3) % p;
    125         
    126         for (int i = 1, t = 1; i <= p; ++i, t = t * bas % p)
    127             C[0] = (C[0] + pow(i, m) * t) % p;
    128     }
    129 
    130     int M[2][2];
    131 
    132     inline void calculateM(void)
    133     {
    134         M[0][0] = pow(bas, p);
    135         M[0][1] = 1;
    136         M[1][1] = 1;
    137         M[1][0] = 0;
    138     }
    139     
    140     int R[2][2];
    141     
    142     inline void calculateR(void)
    143     {
    144         memcpy(R, C, sizeof C);
    145         
    146         for (int t = cnt; t; t >>= 1)
    147         {
    148             if (t & 1)
    149             {
    150                 static int T[2][2];
    151                 
    152                 memset(T, 0, sizeof T);
    153                 
    154                 for (int i = 0; i < 2; ++i)
    155                     for (int k = 0; k < 2; ++k)if (R[i][k])
    156                         for (int j = 0; j < 2; ++j)if (M[k][j])
    157                             T[i][j] = (T[i][j] + R[i][k] * M[k][j]) % p;
    158                 
    159                 memcpy(R, T, sizeof R);
    160             }
    161             
    162             {
    163                 static int T[2][2];
    164                 
    165                 memset(T, 0, sizeof T);
    166                 
    167                 for (int i = 0; i < 2; ++i)
    168                     for (int k = 0; k < 2; ++k)if (M[i][k])
    169                         for (int j = 0; j < 2; ++j)if (M[k][j])
    170                             T[i][j] = (T[i][j] + M[i][k] * M[k][j]) % p;
    171                 
    172                 memcpy(M, T, sizeof M);
    173             }
    174         }
    175     }
    176 
    177     inline void main(void)
    178     {
    179         cnt = n / p;
    180         
    181         calculateC();
    182         calculateM();
    183         calculateR();
    184         
    185         ans = R[0][1];
    186         
    187         for (int i = cnt * p + 1; i <= n; ++i)
    188             ans = (ans + pow(i % p, m) * pow(bas, i - 1)) % p;
    189         
    190         ans = (1LL * ans * pow(2, m)) % p;
    191         ans = (1LL * ans * (m*m - m)) % p;
    192         
    193         printf("%d
    ", (ans + p) % p);
    194     }
    195 }
    196 
    197 signed main(void) 
    198 {
    199     scanf("%d%d%d", &n, &m, &p);
    200     
    201     if (m <= 200)
    202         case1::main();
    203     else
    204         case2::main();
    205 }

    @Author: YouSiki

  • 相关阅读:
    Junit研究心得——准备研究Junit
    Nginx proxying for Tomcat applications
    将工厂模式升华为插件式框架【文摘】
    JIRA中输入验证时,将验证错误“InvalidInputException”写到对应字段顶部,而不是页面顶部。
    Junit研究心得——Junit中的Annotation
    Junit研究心得——Junit中的约定规则
    Java 编程的动态性【文摘】
    插件式设计开发心得(一)【文摘】
    Java SE 6 新特性: Instrumentation 新功能
    php实现首页自动选择语言跳转
  • 原文地址:https://www.cnblogs.com/yousiki/p/6407779.html
Copyright © 2011-2022 走看看