BZOJ 2745: [HEOI2012]Bridge
2745: [HEOI2012]Bridge
Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 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
Sample Output
【样例说明】
总共有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
写了一天的二逼题,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\n", (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\n", (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