「BZOJ 4451」「CERC 2015」Frightful Formula

  给 $n \times n(n \le 200000)$ 的方格,第一列、第一行 $f[1, i], f[i, 1] (\le 10 ^ 6)$ 和 $a, b, c(\le 10 ^ 6)$ ,转移方式为 $f[i, j] = a f[i, j-1] + b f[i-1, j] + c$ ,求 $f[n, n] \mod 1000003$ 。

  考虑每个位置的贡献。

  第一行、第一列的贡献直接 $O(n)$ 算,注意第一步的方向是确定的。

  对于其余位置 $(i, j)$ ,贡献为 $\begin{aligned} c a ^ {n - j} b ^ {n - i} \binom{n - i + n - j}{n - i} \end{aligned}$ 。

  $\begin{aligned} contr & =  \sum_{i = 2} ^ {n} \sum_{j = 2} ^ n a ^ {n - j} b ^ {n - i} \binom{n - i + n - j}{n - i} \\ & = \sum_{i = 0} ^ {n - 2} \sum_{j = 0} ^ {n - 2} \binom{i + j}{i} a ^ i b ^ j  \end{aligned}$

  直观的想法是直接求 $\left\{ a_x \right\}$ 和 $\left\{ b_x \right\}$ 的二项卷积,系数相加就可以得到贡献。

  实现的时候不能直接写 FFT ,因为会爆精度,需要使用「任意模数卷积」的技巧。

  实现:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 #define F(i, a, b) for (register int i = (a), _b = (b); i <= _b; i++)
  4 #define For(i, a, b) for (register int i = (a), _b = (b); i < _b; i++)
  5 #define LL long long
  6 #define db double
  7 inline int rd(void) {
  8     int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1;
  9     int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f;
 10 }
 11
 12 const int S = 600000;
 13 const int P = 1e6+3;
 14 const int p = sqrt(P);
 15 const db PI = acos(-1);
 16
 17 int n, ans;
 18 int fac[S], inv[S], ifac[S];
 19 int m, a[S], b[S], I, c[S];
 20 struct comp {
 21     db x, y;
 22     inline comp(db _x = 0, db _y = 0): x(_x), y(_y) {}
 23     friend inline comp operator + (comp a, comp b) { return comp(a.x + b.x, a.y + b.y); }
 24     friend inline comp operator - (comp a, comp b) { return comp(a.x - b.x, a.y - b.y); }
 25     friend inline comp operator * (comp a, comp b) { return comp(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x); }
 26 }x[S], y[S], x2[S], y2[S], r[S];
 27 int bit, len, rev[S];
 28
 29 inline int C(int n, int m) {
 30     if (m < 0 || n < m) return 0;
 31     return 1LL * fac[n] * ifac[m] % P * ifac[n-m] % P;
 32 }
 33
 34 void FFT(comp *a, int sig) {
 35     For(i, 0, len) if (i < rev[i]) swap(a[i], a[rev[i]]);
 36     for (int i = 2; i <= len; i <<= 1) {
 37         comp wn;
 38         if (sig == 0)
 39             wn = comp(cos(2*PI / +i), sin(2*PI / +i));
 40         else wn = comp(cos(2*PI / -i), sin(2*PI / -i));
 41         for (int j = 0; j < len; j += i) {
 42             comp w(1, 0);
 43             for (int k = 0; k < i / 2; k++) {
 44                 comp x = a[j+k], y = w * a[j+k+i/2];
 45                 a[j+k] = x+y, a[j+k+i/2] = x-y;
 46                 w = w * wn;
 47             }
 48         }
 49     }
 50     if (sig == 1) {
 51         For(i, 0, len)
 52             a[i] = comp(a[i].x / len, 0);
 53     }
 54 }
 55 void contri(int t) {
 56     For(i, 0, len) {
 57         int w = (LL)(r[i].x + 0.5) % P;
 58         c[i] = (c[i] + 1LL * w * t) % P;
 59     }
 60 }
 61
 62 int main(void) {
 63     #ifndef ONLINE_JUDGE
 64         freopen("4451.in", "r", stdin);
 65     #endif
 66
 67     fac[0] = 1;
 68     For(i, 1, S) fac[i] = 1LL * fac[i-1] * i % P;
 69     inv[1] = 1;
 70     For(i, 2, S) inv[i] = 1LL * (P - P / i) * inv[P % i] % P;
 71     ifac[0] = 1;
 72     For(i, 1, S) ifac[i] = 1LL * ifac[i-1] * inv[i] % P;
 73
 74     n = rd(), m = n-2, a[1] = rd(), b[1] = rd(), I = rd();
 75     a[0] = 1; F(i, 2, n) a[i] = 1LL * a[i-1] * a[1] % P;
 76     b[0] = 1; F(i, 2, n) b[i] = 1LL * b[i-1] * b[1] % P;
 77
 78     F(i, 1, n) {
 79         int w = rd();
 80         if (i > 1) ans = (ans + 1LL * w * a[n-1] % P * b[n-i] % P * C(n+n-i-2, n-2)) % P;
 81     }
 82     F(i, 1, n) {
 83         int w = rd();
 84         if (i > 1) ans = (ans + 1LL * w * a[n-i] % P * b[n-1] % P * C(n+n-i-2, n-2)) % P;
 85     }
 86
 87     for (bit = 0, len = 1; len <= m+m; bit++, len <<= 1);
 88     For(i, 1, len) rev[i] = rev[i >> 1] >> 1 | (i & 1) << (bit - 1);
 89
 90     F(i, 0, m) {
 91         int w = 1LL * a[i] * ifac[i] % P;
 92         x[i] = w / p, y[i] = w % p;
 93     }
 94     F(i, 0, m) {
 95         int w = 1LL * b[i] * ifac[i] % P;
 96         x2[i] = w / p, y2[i] = w % p;
 97     }
 98     FFT(x, 0);
 99     FFT(y, 0);
100     FFT(x2, 0);
101     FFT(y2, 0);
102     For(i, 0, len) r[i] = x[i] * x2[i];
103     FFT(r, 1);
104     contri(1LL * p * p % P);
105     For(i, 0, len) r[i] = x[i] * y2[i];
106     FFT(r, 1);
107     contri(p);
108     For(i, 0, len) r[i] = y[i] * x2[i];
109     FFT(r, 1);
110     contri(p);
111     For(i, 0, len) r[i] = y[i] * y2[i];
112     FFT(r, 1);
113     contri(1);
114     For(i, 0, len) c[i] = 1LL * c[i] * fac[i] % P;
115
116     int sum = 0;
117     For(i, 0, len) sum = (sum + c[i]) % P;
118     ans = (ans + 1LL * I * sum) % P, printf("%d\n", ans);
119
120     return 0;
121 }

  利用递归恒等式展开,可以得到线性做法。

  设 $m = n - 2$ 。

  设 $\begin{aligned} g_i = \sum_{j = 0} ^ m \binom{i + j}{i} b ^ j \end{aligned}$ 。

  贡献为 $\begin{aligned} \sum_{i = 0} ^ m a ^ i g_i \end{aligned}$ 。

  递推求 $g$ 。

  当 $b = 1$ 时,根据上指标求和恒等式,有 $\begin{aligned} g_i = \sum_{j = 0} ^ m \binom{i + j}{i} = \binom{i + m + 1}{i + 1} \end{aligned}$ 。

  当 $b \ne 1$ 时,边界为 $\begin{aligned} g_0 = \sum_{j = 0} ^ m b ^ j \end{aligned}$ ,转移为

  $\begin{aligned} g_i & = \sum_{j = 0} ^ m \binom{i + j}{i} b ^ j \\ & = \sum_{j = 0} ^ m \binom{i - 1 + j}{i - 1} b ^ j + \sum_{j = 0} ^ {m - 1} \binom{i + j}{i} b ^ {j + 1} \\ & = g_{i - 1} + b(g_i - \binom{i + m}{i}) b ^ m \\ & = g_{i - 1} + bg_i - b ^ {m + 1} \binom{i + m}{i} \end{aligned}$

  解得 $\begin{aligned} g_i = \frac{b ^ {m + 1} \binom{i + m}{i} - g_{i - 1}}{b - 1} \end{aligned}$

  实现:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define F(i, a, b) for (register int i = (a), _b = (b); i <= _b; i++)
 4 #define For(i, a, b) for (register int i = (a), _b = (b); i <= _b; i++)
 5 inline int rd(void) {
 6     int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1;
 7     int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f;
 8 }
 9
10 const int N = 200005;
11 const int P = 1e6+3;
12
13 int fac[P], inv[P], ifac[P];
14 int n, a[N], b[N], c;
15 int ans;
16 int m, g[N];
17
18 inline int C(int n, int m) {
19     if (m < 0 || n < m) return 0;
20     return 1LL * fac[n] * ifac[m] % P * ifac[n-m] % P;
21 }
22
23 int main(void) {
24     #ifndef ONLINE_JUDGE
25         freopen("4451.in", "r", stdin);
26     #endif
27
28     fac[0] = 1;
29     For(i, 1, P) fac[i] = 1LL * fac[i-1] * i % P;
30     inv[1] = 1;
31     For(i, 2, P) inv[i] = 1LL * (P - P / i) * inv[P % i] % P;
32     ifac[0] = 1;
33     For(i, 1, P) ifac[i] = 1LL * ifac[i-1] * inv[i] % P;
34
35     n = rd(), a[1] = rd(), b[1] = rd(), c = rd();
36     a[0] = b[0] = 1;
37     F(i, 2, n) a[i] = 1LL * a[i-1] * a[1] % P;
38     F(i, 2, n) b[i] = 1LL * b[i-1] * b[1] % P;
39
40     F(i, 1, n) {
41         int w = rd();
42         if (i > 1) ans = (ans + 1LL * w * a[n-1] % P * b[n-i] % P * C(n+n-i-2, n-2)) % P;
43     }
44     F(i, 1, n) {
45         int w = rd();
46         if (i > 1) ans = (ans + 1LL * w * a[n-i] % P * b[n-1] % P * C(n+n-i-2, n-2)) % P;
47     }
48
49     m = n-2;
50     if (b[1] == 1) {
51         F(i, 0, m)
52             g[i] = C(i+m+1, i+1);
53     }
54     else {
55         F(j, 0, m) g[0] = (g[0] + b[j]) % P;
56         F(i, 1, m)
57             g[i] = (1LL * C(i+m, i) * b[m+1] - g[i-1]) % P * inv[b[1] - 1] % P;
58     }
59     int sum = 0;
60     F(i, 0, m) sum = (sum + 1LL * g[i] * a[i]) % P;
61     ans = (ans + 1LL * c * sum) % P, printf("%d\n", ans);
62
63     return 0;
64 }
时间: 2024-11-03 22:05:25

「BZOJ 4451」「CERC 2015」Frightful Formula的相关文章

「Codechef April Lunchtime 2015」Palindromeness

「Codechef April Lunchtime 2015」Palindromeness 解题思路 : 考虑对于回文子串 \(s\) 贡献的定义: \[ value_s = [\ s[1,\lfloor \frac{|s|}{2}\rfloor]\text{ is palindrome}]\times value_{s[1,\lfloor \frac{|s|}{2}\rfloor]} + 1 \] 也就是说对于每一个回文子串,只需要判断其前一半的字符是不是回文串并得到贡献即可. 于是建出回文树

SSH连接时出现「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」解决办法

用ssh來操控github,沒想到連線時,出現「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」,後面還有一大串英文,這時當然要向Google大神求助啦!收尋了一下,終於被小弟找到原因了- @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@

「笔记」「ubuntu」mint个人shell样式脚本

alias ll='ls -al' use_color=false # Set colorful PS1 only on colorful terminals.# dircolors --print-database uses its own built-in database# instead of using /etc/DIR_COLORS.  Try to use the external file# first to take advantage of user additions. 

「なそう」と「なさそう」

天気予報は雨マークだったのに.空は見事な快晴. 雨の匂いもしないそんなときに一言. 「雨.降らなさそうですね」 皆様はこの一言に違和感を感じますか? 否定形と助動詞「そうだ」 目で見たことや.耳で聞いたこと.推定したことを表す助動詞「そうだ」ですが.前に否定形がきたときに「さ」を入れるか入れないかで戸惑うことはありませんか? 例えば次の三つの文をご覧ください. 「雨は降らなさそうですね」 「今日は定時で帰れなさそうだ」 「ごめん.電車が遅れてる!間に合わなさそう」 「こんなに蓄えがあったら当分は

【翻译】西川善司「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,前篇(1)

http://www.4gamer.net/games/216/G021678/20140703095/ 新连载「实验做出的游戏图形」,是聚焦在特定游戏的图形上, 对它的结构和使用的技术解说为主旨.之前笔者连载的「西川善司的3D游戏入迷」,覆盖范围都很广,而与特定游戏强关联的技术解说,会在今后的新连载中处理. 作为纪念的第一回选择的,是Arc System Works开发的,2014年2月在街机上运作的格斗游戏「GUILTY GEAR Xrd -SIGN-」 全3D图形的GUILTY GEAR

【翻译】西川善司的「实验做出的游戏图形」「GUILTY GEAR Xrd -SIGN-」中实现的「纯卡通动画的实时3D图形」的秘密,后篇

http://www.4gamer.net/games/216/G021678/20140714079/ 连载第2回的本回,  Arc System Works开发的格斗游戏「GUILTY GEAR Xrd -SIGN-」解说的后篇送到了.前篇的最后预告的那样,本回,是只能看到Anime的3D图形的2D格斗游戏产生所采用的细小方法为中心的介绍. 变形的几何体,替换几何体 GUILTY GEAR Xrd -SIGN-的图形,看上去是Cel Anime风格,并不是什么都采用Toon Shader.这

七牛云荣获「2018 中国云计算产业领军企业」称号

12 月 20 日,以「驾驭数字化转型走进智能互联时代」为主题的 2018 中国软件大会暨首届 CIO 创新峰会在北京新世纪日航饭店举行. 作为引领中国软件和信息技术服务产业发展的风向标和产业年度盛典,会上,隆重颁发了「2018 中国云计算产业领军企业」奖项.七牛云受邀出席,凭借其在云计算服务领域取得的优异成绩,荣获「2018 中国云计算产业领军企业」称号. 七牛云荣获「2018中国云计算产业领军企业」称号 中国软件大会自创办以来,一直紧跟时代步伐,把握行业热点,保持着高水准的思想内容和前沿观察

「十二省联考 2019」字符串问题

「十二省联考 2019」字符串问题 解题思路 傻逼题.. 考虑问题转化为一个A串向其支配的所有B串的后缀A串连边,如果有环答案 \(-1\) 否则是这个 \(\text{DAG}\) 上最长路径,直接建图是 \(n^2\) 的,考虑优化建图即可. 由于 \(A,B\) 都是原串的一个子串,那么对原串的反串建 SAM,一个子串的后缀就是其所在节点上比它长的串以及,其子树里的所有串. 首先将所有 \(A,B\) 串在 SAM上用倍增定位并新建节点,把SAM上每个节点拆成入点和出点,对于SAM每一个节

「十二省联考 2019」字符串问题 解题报告

「十二省联考 2019」字符串问题 当场就去世了,我这菜人改了一下午 考虑一个A,B之间的连边实际表示了两个A之间的有向边,然后把A的连边处理好,就转成了拓扑排序找环+最长链 但是边数很多,考虑优化连边 A,B之间的连边显然没法优化的,考虑一个B可以表示所有它的后缀A 把串反向建出SAM,然后一个B的后缀就是par树的子树 可以拿倍增定位 好了这题就没了 注意到一个事情,定位的点可能重复,于是对SAM拆点,每个点挂一个vector表示一个A或者B的点在SAM的这个位置 然后考虑如何连边 一个B所